2

I have this order:

class Order
{
    long Id;
    string Concept;
    string Comments;
    //another properties



  UpdateConcept(string paramConcept) {...}
  UpdateComments(string paramComments) {...}
}

In the domain entity, I have to have one method for each property that I can update.

But my doubt is in the application layer and UI layer. Suponse that in the UI I have a form that shows the information of the order, and the user see that the concept and the comments have to be update to correct some error.

Do I have to update first the concept and then the comments in two separate actions or I could have a generic update that update all the data in a one process?

Because in my case, I have a UI (WPF application for example) that use my application layer in which I have the OrderService. This is the order service if I would have a generic update method:

//Application layer
public OrderService
{
    public OrderService(IUnitOfWork paramUoW)
    {
         _UoW = paramUoW;
    }

    IUnitOfWork _UoW;



    Update(string paramConcept, string paramComments)
    {
        Order myOrder = _UoW.OrderRepository.Get(1);
        myOrder.UpdateConcept(paramConcept);
        myOrder.UpdateComments(paramComments);
        _UoW.Commit();
    }    
}

In this case, I only have to call the database one to save the data. So the user could have edit both fields in the form and when he considerate all is correct, to click the button accept to save the data. This accept button would call the update method of the service in the application layer.

If I would have one method for each update of each field, I would in the service something like that:

//Application layer
public OrderService
{
    public OrderService(IUnitOfWork paramUoW)
    {
         _UoW = paramUoW;
    }

    IUnitOfWork _UoW;



    UpdateComments(string paramComments)
    {
        Order myOrder = _UoW.OrderRepository.Get(1);
        myOrder.UpdateComments(paramComments);
        _UoW.Commit();
    }

    UpdateConcept(string paramConcept)
    {
        Order myOrder = _UoW.OrderRepository.Get(1);
        myOrder.UpdateComments(paramComments);
        _UoW.Commit();
    }   
}

So I guess the option it is to call the method UpdateComments when the user leave the comments field and call the the method to update the concept when it will leave the field of concept. But it is duplicate the calls to the database.

How could handle this case, when it is needed to update various properties of the entity?

Perhaps I could separate in the service of the application layer the 3 steps of the update, load the order, update the data and commit the data, something like that:

//Application layer
public OrderService
{
    public OrderService(IUnitOfWork paramUoW)
    {
         _UoW = paramUoW;
    }

    IUnitOfWork _UoW;


    GetOrder(long paramId)
    {
        _UoW.OrderRepository.Get(paraId);
    }


    UpdateComments(string paramComments)
    {
        Order myOrder = _UoW.OrderRepository.GetLocal(1);
        myOrder.UpdateComments(paramComments);
    }

    UpdateConcept(string paramConcept)
    {
        Order myOrder = _UoW.OrderRepository.GetLocal(1);
        myOrder.UpdateComments(paramComments);
    }   

    void Commit()
    {
        _UoW.Commit();
    }
}

In this case, I would separate the load of the entity to update, and the methods to update only update the entity without commit, and when the user is ready to confirm the update, it could click the accept button, that calls the method commit of the service.

But in sumary, I would like to know how to handle the case that various properties of the entity need to be updated.

Thanks.

6
  • 1
    There are API and UI design solutions that allow for delta changes. That is for only sending the fields that have changed. The standard approach when your API doesn't support deltas is to send the entire model, including the unchanged fields. You are right to assume this is not a good practise but having individual property mutators on your service is too cumbersome to maintain without a high level of code automation. Commented Oct 19, 2022 at 21:07
  • You ask a lot of questions on DDD, which is good, but looking at this question and the code you seem to be missing some of the basics. Do some research on ubiquitous language (update is not typically a term domain experts use), task based ui, making concepts explicit (why you need comments on an order, you might be missing a domain concept).
    – Rik D
    Commented Oct 19, 2022 at 21:57
  • If there is a specific task that requires the user to change multiple properties at once, why not facilitate that? Simply make a method with a descriptive name that reflects the task that updates multiple properties.
    – Rik D
    Commented Oct 19, 2022 at 22:00
  • @RikD Well, perhaps the experts are not the same than final users, but final users want a field for concept (like a title or small description of the order) and comments to indicate some generic things. Comments it is not so odd for me because really in many pages when I will order something, I can write some comments related with the order. Commented Oct 20, 2022 at 9:25
  • Your first approach (Update(string paramConcept, string paramComments)) is the correct way to do it, just give the method a less generic name. It's the approach with the least amount of coupling. The interface of your service shouldn't just duplicate the interface of the Order (shouldn't have all the same methods), generally speaking. That almost defeats the point of having a service. The methods on the service should reflects the tasks or usage patterns of your app's users, not small steps within those. So, something like UpdateOrderNotes or AddOrderItems. Commented Oct 21, 2022 at 23:45

1 Answer 1

3

This issue is not specifically related to DDD at all. What you are dealing with at this level is a specific interface between your API and the UI and this concept is a challenge for all design patterns that try to decouple the user interface from the business logic and or data access.

A common approach is to pass the whole model across the boundary from the view to the service layer. We would normally design the models to match discrete units of work, effectively grouping properties that commonly get captured or processed together. The aim is to reduce the number of individual field changes to a point where it becomes relatively "safe" to assume that the user, when submitting a change to a single field, has also reviewed the other fields and has authority to accept that the other properties, although un-changed, are in a correct state.

  • When the above assumption does not work, so the user only has visibility or authority to make changes to specific fields, then this represents a new, smaller unit or sub-unit of work. In this case we make specific end points in the service that accept this smaller sub-set of fields.
  • Sometimes the unit of work is a single field, if the interface only allows the user to interact with a single field, then it makes sense to support this as a single property access on the service.
    • You would not normally try to support individual updates to all properties in the service as the primary mode of interaction. You might generate this level of code access as a feature to support extreme or agile programming practises, but it is not generally an efficient way to manage the life-cycle of your data records.

In DDD terms, this grouping of fields or objects that we pass to the service is an Aggregate. Read more on DDD Aggregates here... WPF using the MVVM pattern is designed specifically to plug into this Aggregate concept, the Model is the Aggregate and the individual properties in the View are bound to the View Model that in turn manages the state of the Model (DDD Aggregate).

Do I have to update first the concept and then the comments in two separate actions or I could have a generic update that update all the data in a one process?

From a hypothetical viewpoint, this is entirely up to your design. The term Aggregate does imply that there are more than one but there are valid use cases where your aggregate is a single field. Focus on the user interactions, was there a single save button to commit the changes in the user interface to the service layer? If so then the aggregate should model all of the possible changes for that user interaction.

If your service requires each property to be set individually, and for each interaction it re-loads the data from the database for that record just to edit a single field, then this can introduce some significant performance issues for workloads where the user needs to edit many fields.

The other problem with a service layer that exposes each individual property as individual contracts is that you will significantly increase the surface area of your services, that's a lot of additional testing, documentation and physical lines of code that all need to be managed. It's is also an increased surface area to manage security risks. It's just a lot of plumbing to manage, like having each one of the taps or faucets in your house directly piped out to the mains in the street.

Passing the whole model is a compromise that provides gains in performance and responsiveness of the application as well as SDLC efficiencies. It does however pose new challenges, what to do about the fields that have not changed? And how do we handle the case of two users editing the same record? This issue is known as concurrency.

  • Concurrency is still an issue with individual property updates, but it has less risk in that mode.

Dealing with concurrency is out of scope for this, but there are standard patterns around it that your current service implementation is not taking into account, we might call your current implementation Extremely Optimistic, because you're not even checking ;)

Perhaps I could separate in the service of the application layer the 3 steps of the update, load the order, update the data and commit the data...

What you are describing is the process from the point of the user interface. If your user interface is written in WPF, then traditionally the VM will load the data from the service via GetOrder into the View, the VM tracks the changes and then at a point sends that data back to the service.

You still have to deal with concurrency in the service, but you don't want the service to have to manage the life-cycle or transient state of the aggregates while the data is being collected, that is the purpose of the View Model, to collect the aggregate data.

  • Do not be tempted to use your Service as the View Model. If your service exposes each property individually this might seem like an efficiency, but that is only if you try to track the fields independantly and write them to the service independantly after a specific trigger event.
  • In WPF terms, do not use your service as the DataContext. To do so will force/allow you to incorporate too many visual state management concepts into your service layer. Just don't go there.

There are service implementation patterns that help to reduce the bytes being transferred to only those fields in the user interface that have changed. This is often a motivation behind the thought process that leads to a service layer that has individual properties exposed. The concept is still the same, there is a specific aggregate that the service layer will accept for a given action end point, but the implementation will allow for only some of the properties or objects to be provided for the content of the aggregate payload, or it will include some metadata to indicate which specific fields should be updated. This is still part of the overall concurrency management.

  • The specifics on these types of implementations are out of the scope of DDD itself, that level of the interface is really a protocol discussion, about how the different layers interact with each other.

Focus on your aggregates first, then post questions about your specific implementation requirements or concerns as you reach them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.