32

In a Multi-layer project with Domain layer (DL)/Business (Service) Layer (BL)/Presentation Layer (PL), what is the best approach to deliver Entities to the Presentation Layer?

DO => Domain Object;
DTO = Domain Transfer Object;
VM => View Model;
V => View;

Option 1:

DL => DO => BL => DTO => PL => VM => V

This option seems to be the Best Practice but also seems heavy to mantain.

Option 2:

DL => DO => BL => DTO => PL => V

This option seems not very good practice but as DTO are almost identical to the VM, we can pass it directly to the View and it's less painfull to implement and mantain.

Is this option also reliable for multiple layouts, for example, for Mobile Devices I may need less information from the BL, so I will need a diferent VM for this particular Layout?

ebram khalil
  • 8,252
  • 7
  • 42
  • 60
Patrick
  • 2,995
  • 14
  • 64
  • 125

5 Answers5

13

It's OK to pass the DTO to the view. If you need to change or enhance the DTO then create a ViewModel. A common scenario would be to add links. It's also OK for the ViewModel to reference the DTO as a complex property.

Max Toro
  • 28,282
  • 11
  • 76
  • 114
  • Thanks for your answer, but it would not be better to use always the same arquitecture rather than using in some situations option 1 and in others option 2? – Patrick Oct 13 '12 at 15:20
  • 5
    I would say use the right technique to complete the task at hand. I wouldn't recommend creating a view model for the sake of creating a view model. – kevin_fitz Oct 13 '12 at 15:44
  • 2
    @Patrick for consistency yes. That's why I said 'OK' and not 'best practice'. My choice is to always create ViewModel and reference the DTO as a complex property. – Max Toro Oct 13 '12 at 19:51
  • @Max thanks, and how do you suggest that I initialize the mapping in the service layer using automapper? – Patrick Oct 15 '12 at 10:35
  • 1
    I will highly recommended this video from Ayende http://www.youtube.com/watch?v=0tlMTJDKiug It will surely help to understand concept in and out and probably impact a lot of things about development and DDD pattern. It is more than few POCO objects. – kunjee Jan 09 '13 at 10:10
  • 1
    This is definitely wrong. Anti pattern and anti everything in the world. – Ashi Feb 16 '22 at 08:20
4

If you are going to have different views that require different data from your Dto it sounds like you might benefit from having different view models for these and map your Dto to these.

One of the ideas behind this is to allow greater freedom to change your view model, knowing it will not have an impact on any other part of your application. If your Dto is being used in several views then each change to your Dto would require you to test each view.

dove
  • 20,469
  • 14
  • 82
  • 108
  • Thanks for your answer, so you are saying that each Dto should have its own VM in a mantainance point of view? – Patrick Oct 13 '12 at 15:21
  • 1
    You might use the Dto directly in some admin views or when it is only used in one place. Creating the view models once you see a need to use dto in more than one place. On the other hand if you are going with the Domain, Dto, Viewmodel approach it doesn't take that long to create the extra objects (with automapper between) and saves any time trying to decide which to use. Makes it clearer for anyone else on the project to know what to do. IMHO, this might save more time in the long run and make your code a little maintainable. Having only viewmodels in views helps in several ways... – dove Oct 13 '12 at 15:46
  • 1
    as always it depends, on your domain, team and size of project. – dove Oct 13 '12 at 15:47
  • And how would you create the mapping using Automapper at the service level since there is no Global.Asax in this level? I use the App_start folder to put everystarting stuff and call then from Global.asax in the presentation layer, but where should I create them in the service project? maybe with the DTO definition? – Patrick Oct 14 '12 at 14:54
  • 1
    You could store the Automapper profile near the Dto definition but you would still configure on application start like the others. up to you – dove Oct 14 '12 at 15:10
  • I would prefer to keep DTO configuration in the service layer, beacause if tomorrow I need a Win32 aplication to "talk" to the service layer, I would not be able to put the mapping up and running, correct? I don't know is where do I can create the profiles because I only know to do it in the Global.asax – Patrick Oct 15 '12 at 10:34
  • 1
    you can have the profile in the service layer, where the details of the mapping is stored. However, you will have to also call the automapper configuration within the windows service, a one liner or not much more. – dove Oct 15 '12 at 10:39
  • What do you mean about "Windows service", I have build the profiles and mapping but for now and based and my knowledge I have to put the AutoMapperConfiguration.Configure(); on the global.asax – Patrick Oct 15 '12 at 14:38
  • 1
    that's fine. now you must put the AutoMapperConfiguration.Configure() into the start of your Win32 application (I had assumed this was a service). – dove Oct 15 '12 at 15:10
  • Width 2 Mapper.Initialize(x => { x.AddProfile, one for Service layer, onde for Web layer, automapper raise an erro. Aprently it only accepts onde initialize, and it seems that it has to be in the global.asax. Another option is to create directly the mapping without profiles, what do you think? – Patrick Oct 15 '12 at 21:50
2

To me this decision is based on where I put my validation logic.

Scenario #1: Adding a data annotation in viewmodel (in UI layer) greatly simplifies programming. Mostly there will be one on one mapping between DTO and view model. In such cases Option 1 is good. DL => DO => BL => DTO => PL => VM => V

Scenario #2) If the validation is not attached to ViewModels then DTO is replaced with ViewModel and ViewModel resides in Business layer. DL => DO => BL => VM => PL => V

Scenario #2 can be perfect in situations where validation logic resides in Domain Models. And these models are used by many UI applications. UI just lists the errors in a list, as given by the business layer (not very user friendly though). As the application undergoes business rules change we only change the domain model. Again database related validations can be auto generated through EF(if using .net), even less scope for change.

Blue Clouds
  • 7,295
  • 4
  • 71
  • 112
2

I have created several middle size applications with the following approach and sleep very well at night ;-)

Entity <=> Service <=> Model

Please note, those 3 layers must be separated by Packages/Modules/Assemblies and be a backend only.

Entity: Domain Layer, Domain Object, Repository, Database Model, Data Object Layer (DAL)

Service: Business Logic Layer (BLL), Business Service Layer

Model: Data Transfer Object (DTO), Presentation Layer, View Model (please remove presentation/view from your dictionary)

View is a separate project (PWA, Progressive Web Application) which uses API and does not require any backend code support. It has its own View Models and Business Logics inside.

Example:

MyProject.Entities (DAL):

[Table("customers")]
public class Customer
{
    // Used to instantiate the proxy by DbContext
    protected Customer() {  }
    
    // Used by developers
    public Customer(string name) : this()
    {
        Id = IdGenerator.Next();
        SetName(name);
    }
    
    [Key]
    public virtual string Id { get; protected set; }

    public virtual string Name { get; protected set; }
    
    [Column("summary")]
    public virtual string Description { get; set; }

    public void SetName(string name)
    {
        if (string.IsNullOrEmpty())
            throw new ArgumentNullException(nameof(name));

        Name = name;
    }
}

MyProject.Models (DTO):

public class IdModel
{
    [Required]
    public string Id { get; set; }
}

public class CreateCustomerModel
{
    [Required]
    [MaxLength(50)]
    public string Name { get; set; }
}

public class EditCustomerModel : IdModel
{
    [Required]
    [MaxLength(50)]
    public string Name { get; set; }

    public string Description { get; set; }
}

MyProject.Services (BLL):

public class CustomerService
{
    private readonly DbContext _db;
    private readonly IMapper _mapper;

    public CustomerService(DbContext db, IMapper mapper)
    {
        _db = db;
        _mapper = mapper;
    }

    public async Task<EditCustomerModel> Get(IdModel model)
    {
        var customer = await _db.Get<Customer>(model.Id);

        return _mapper.Map<EditCustomerModel>(customer);
    }

    public async Task<IdModel> Create(CreateCustomerModel model)
    {
        var customer = new Customer(model.Name);

        await _db.SaveOrUpdate(customer);

        return _mapper.Map<IdModel>(customer);
    }

    public async Task Edit(EditCustomerModel model)
    {
        var customer = await _db.Get<Customer>(model.Id);

        customer.SetName(model.Name);
        customer.Description = model.Description;

        await _db.SaveOrUpdate(customer);
    }

    public async Task Delete(IdModel model)
    {
        await _db.Remove<Customer>(model.Id);
    }
}

The above example is just a simple CRUD service. But in the same way you can implement any Domain Services eg. CustomerInvoiceService:

[Description("Creates a new customer and invoices it. Returns invoice Id.")]
public async Task<IdModel> CreateCustomerAndInvoiceIt(CreateCustomerInvoiceModel model)
{
    model.InvoiceForm.CustomerId = await _customerService.Create(model.CustomerForm);
    
    var invoiceId = await _invoiceService.Create(model.InvoiceForm);

    return invoiceId;
}

Just expose API/services and let frontend devs freely do their job completely separated from the backend. If it's a desktop app or the front end is made using backend language like c#/java/Go (which is very bad), they must build their own Frontend Logic Layers and Models and never allow them to mix Domain/Business Layers with Presentation Layers together in any situation. Frontend is a completely separated thing and, as someone mentioned Views above, I would suggest to remove that word from the BLL dictionary at all.

IMHO

ADM-IT
  • 3,719
  • 1
  • 25
  • 26
  • Hi, Thank You for your answer. Can you just help me to identify the DTO in your example? Thanks. – Patrick Jun 17 '22 at 10:40
  • 1
    `MyProject.Models` are DTOs. Their purpose is to transfer data from one service into another (eg. `CreateCustomerModel`). `MyProject.Entities` are Domain objects. Their purpose is to keep/persist the state of the business logic. – ADM-IT Jun 17 '22 at 11:06
  • And in the controller, what model do we send to the View? – Patrick Jun 20 '22 at 14:41
  • 1
    Sorry but there are no controllers, controllers are in MVC. If you creating an api for your needs you can use controllers, but they are not necessery. Using minimal api approach (https://learn.microsoft.com/en-us/aspnet/core/tutorials/min-web-api) you can register your `public` services without controllers. In my opinion controllers are redundant. – ADM-IT Jun 21 '22 at 11:14
  • 1
    But if you still think about the controllers (even as I said they add an extra level of complexity), then `MyProject.Models` (DTOs) are used to transfer your data from view into BLL and back. `public async Task MyControllerAction(MyFormModel model) { return _businessLogic.Process(model); }`. – ADM-IT Jul 21 '22 at 12:47
1

See here my reply : https://stackoverflow.com/a/14059156/1288063

You say : This option seems to be the Best Practice but also seems heavy to mantain.

heavy to implement perhaps, juste few lines of code to duplicate most the time, but to maintain surely not.

Community
  • 1
  • 1
riadh gomri
  • 869
  • 1
  • 15
  • 21