Can I use Domain Objects directly in Presentation

2020-05-03 12:47发布

问题:

I originally had the following setup:

Presentation Layer using --> Service Proxies generated via WCF --> Actual Services assembly (where proxy was generated from; hosted via IIS) --> Domain Layer (business logic) --> DAL

That was it initially but found out that Service and DAL should also access Domain Layer. Now I have a scenario where in the Presentation Layer, I have a datagrid that computes grades, and there are rules and computations which I believe should be in the Domain Objects.

My questions are:

  1. Should the Presentation Layer reference the Domain Objects too? Is this common practice?

  2. If yes, why would I need a Service Layer if I can directly access the Domain Objects? Or am I thinking in traditional n-Layer? If no, how would I go about this?

Feedback would be very much appreciated.

UPDATE/EDIT:

@Yorro: Thank you for the response. In your Option 1, that is my problem. I can't send the values to be computed to the server. I need to have it reflected immediately in the UI.

For example, I have a domain object:

public class Grade
{
    List<CriterionGrade> children = new List<CriterionGrade>();

    public decimal FinalGrade
    { 
        get
        {
            decimal final = 0;
            foreach (CriterionGrade grade in CriterionGrades)
                total += line.LineTotal;

            return total;
        }
    }

    public IEnumerable<CriterionGrade> CriterionGrades
    {
        get { return children; } 
    }

    public void AddCriterionGrade(CriterionGrade grade)
    {
        children.Add(grade);
    }
}

What I am currently doing now is I have a DTO for Grade which includes the children but, being a DTO, no business logic. This DTO is on another machine along with Service Layer which I get a reference using WCF. Now my Presentation is WPF. So I bound the DTO to my DataGrid.

What I want to achieve is whenever the user changes the grade in the DataGrid, the FinalGrade rule should be enforced (this, the FinalGrade, is displayed in the UI too). Now since the proxies generated doesn't contain the Logic/Rules, I don't have access to that rule. And my requirement is I need the FinalGrade computed and shown on UI after any change.

Any ideas of how this is commonly resolved without duplicating the logic? The only thing I can think of is to reference the actual Domain object and put the computation in a service since the DataGrid is bound to the proxies but I am not sure if that is correct or ideal.

回答1:

Referencing other layers in the presentation

The presentation can reference other layers, this is a common practice because some layered architectures implement IoC and DI, but that is another topic.

Even if the presentation references the other layers, that doesn't mean the presentation can directly access the domain layer. You still have to consider whether to commit to a clean cut logical separation (or loosely coupled multi-layer architecture). The presentation layer still communicates with the service layer, whether they are separated or not.

Why use the service layer if you can directly access the domain?

In DDD, you create a separate layer just for the domain right? You properly encapsulate and abstract this Domain Layer (using services) so that it becomes loosely coupled from the rest of the application (presentation), the purpose of this loosely coupled design is that any changes in the domain does not affect the entire application. This is the main selling point of DDD.

If you reference the domain all over the place, then changes in the domain requires you to change the rest of your application, like ripple effect. But if the layers are properly separated, each layer can evolved independently from each other, this is perfect for team based development.

If your presentation is physically separated from the domain (nTier architecture) and still want to adhere to DDD, I have two suggestions:

Option 1

You can create a "thin client". It is called "thin" because it does not do any computations or any hard work, the server does that for them. If the client wants to compute something, it just sends the parameters to the server (via Web API or WCF) then receives the computed result for display.

Option 2

If your parameters are too big (entire datagrid). You can include the service layer in your presentation. But the presentation and service are still separated, logically.

EDIT1:

Actually, you can send the entire datagrid in JSON format, and receive the final grade as response. If you are concerned about performance, ask yourself:

  • how many users are we talking about here? hundreds of concurrent users?
  • how big is the datagrid? hundreds of rows?
  • Is it really slow as in more than 60 seconds per request? Have you even measured it?
  • how big is the difference?

These are the questions that you should answer so you won't fall into the "premature optimization" trap.



回答2:

It depends.

The "Service" you mentioned is called "Application Service" in DDD.

If your presentation layer is physically separated from the domain layer, you might want to add a "service" layer (WCF service, REST service etc) because you have no way to reference the domain models in this case.

But if your layers are only logically separated, then it's fine to reference the domain objects in the presentation layer (in this case, you can remove your "service" layer).

The "Application Service" layer is just a "Facade". It can be used to hide the complexity of the domain layer by exposing much simpler application-specific APIs to your front-end applications (Websites, WPF Applications, etc). So actually there's no right or wrong to have it. You have to ask yourself, is your domain layer so complex that you have to add an "Application Service" layer to hide the complexity? I would prefer to "Keep It Simple Stupid" in the beginning.

BTW: I would prefer to use MVVM pattern in the presentation layer, especially when you are already using a MVVM framework like WPF. In this case, you handle UI logic in the View Model, and delegate domain logic to the domain layer.