Seperation of models in a web api application
Asked Answered
T

2

9

My team devolops a web api application using entity framework, The Gui is developed by a seperate team.

My question is how should the models be defined? Should we have two projects - one for domain models (database entities) and one for Dtos which are serializable?

Where should the parsing from Dto to domain models should happen and when should it happen the opposite way?

Moreover, sometimes all the data is needed to be sent to the clients.. Should a Dto be created for those cases as well? Or should I return a domain model?

Treadle answered 1/3, 2016 at 13:22 Comment(0)
C
9

Generally speaking, it's a good idea to not let your entities (database models) leak out of your database layer. However, as with everything in software - this can have its downfalls. One such downfall being is that it starts to increase complexity of your data layer as it involves mapping your entities to their DTO within your database layer, ultimately leaving repositories that are full of similar methods returning different DTO types.

Some people also feel that exposing IQueryables from your data layer is also a bad thing as you start to leak abstractions to different layers - though this has always seemed a little extreme.

Personally, I favour what I feel is a more pragmatic approach and I prefer to use a tool like AutoMapper to automatically map my entities to my DTOs within the business logic layer.

For example:

// Initial configuration loaded on start up of application and cached by AutoMapper
AutoMapper.Mapper.CreateMap<BlogPostEntity, BlogPostDto>();

// Usage
BlogPostDto blogPostDto = AutoMapper.Mapper.Map<BlogPostDto>(blogPostEntity);

AutoMapper also has the ability to configure more complex mapping, though you should try and avoid this if possible by sticking to flatter DTOs.

In addition, another great feature of AutoMapper is the ability to automatically project your entities to DTOs. This results in much cleaner SQL where only the columns within your DTO are queried:

public IEnumerable<BlogPostDto> GetRecentPosts()
{
    IEnumerable<BlogPostDto> blogPosts = this.blogRepository.FindAll().Project(this.mappingEngine).To<BlogPostDto>().ToList();

    return blogPosts;
}

Moreover, sometimes all the data is needed to be sent to the clients.. Should a Dto be created for those cases as well? Or should I return a domain model?

DTOs should be created for those. Ultimately you don't want your client depending on your data schema, which is exactly what will happen if you expose your entities.

Alternatives: Command/Query Segregation

It behooves me to also highlight that there are also some other alternatives to a typical layered architecture, such as the Command/Query Segregation approach where you model your commands and queries via a mediator. I won't go into it in too much detail as it's a whole other subject but it's one I would definitely favour over a layered approach discussed above. This would result in you mapping your entities to your DTOs directly within the modelled command or query.

I would recommend taking a look at Mediatr for this. The author, Jimmy Bogard who also created AutoMapper also has this video talking about the same subject.

Cirrocumulus answered 1/3, 2016 at 13:32 Comment(0)
F
2

I've had similar requirements in several projects and in most cases we separated at least three layers:

Database Layer

The database objects are simple one-to-one representations of the database tables. Nothing else.

Domain Layer

The domain layer defines entity objects which represent a complete business object. In our defintion an entity aggregates all data which is directly associated to the entity and can not be regarded as a dedicated entity.

An exmaple: In an application which handles invoices you have a table invoice and invoice_items. The business logic reads both tables and combines the data into a entity object Invoice.

Application Layer

In the application layer we define models for all kind of data we want to send to the client. Pass-through of domain entity objects to save time is tempting but strictly prohibited. The risk to publish any data which shouldn't be published is too high. Furthermore you gain more freedom regarding the design of your API. That's what helps you to fit your last requirement (send all data to the client): Just built a new model which aggregates the data of all domain objects you need to send.

This is the minimum set of layers we use in all projects. There were hundreds of cases where we've been very happy to have several abstraction layers which gave us enough possibilities to enhance and scale an application.

Frigidaire answered 1/3, 2016 at 14:17 Comment(7)
But wouldn't the domain objects be similar to the data objects? Because in the tables models Invoice will have a list of invoices items or something similar, am I wrong?Treadle
Yes in most cases domain and database objects will be nearly similar but not identical. We have some cases where we modify or optimize the data in the domain layer a little bit. For instance the database stores the tax rate of a Product only if it differs from the default tax rate. But in the application layer we do not want to look for the default tax rate any time we handle a product so we add the default tax rate in the domain layer. Thats why the data type of Product.TaxRate in domain layer is double but in data base layer it is double?.Frigidaire
So your maps are Dto -> Domain -> Datbase Database -> domain -> Dto Isnt it too many mappings for a simple get operation for example?Treadle
Yes I know, at first it may seem a little bit overengineered. But in practice the advantages of this layering concept justify the effort. It is an investment in the scalability and maintainablity of your application. You can change your application whithout necessarily changing your domain objects. And you can change your domain objects without necessarily changing your database.Frigidaire
I understand, and all the mappings happen in the domain layer? This layer has reference to all Models projects?Treadle
No the model projects reference the domain layer and the domain layer references the database layer. In the domain layer database objects are mapped to domain entity objects. In the application layer domain entity objectes are mapped to application models.Frigidaire
I don't follow. Just to make sure, we're talking about 6 projects? One for each layer + one for the layer's models? Why do all models projects reference the domain layer?Treadle

© 2022 - 2024 — McMap. All rights reserved.