Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In DDD how to pass Value Objects via DTO?

In my domain each Domain Entity may have many Value Objects. I have created value objects to represent money, weight, count, length, volume, percentage, etc.

Each of these value objects contains both a numeric value and a unit of measure. E.g. money contains the monetary value and the currency ($, euro,...) , weight contains the numeric value and the unit of weight (kilo, pound, ...)

In the user interface these are displayed side-by-side as well: field name, its value followed by its accompanying unit, typically in a properties panel. The domain entities have equivalent DTOs that are exposed to the UI.

I have been searching for the best way to transfer the value objects inside the DTOs to the UI.

  1. Do I simply expose the specific value object as a part of the DTO?
  2. Do I expose a generic "value object"-equivalent that provides name/value/unit in a DTO?
  3. Do I split it into separate name/value/unit members inside the DTO, just to reassemble them in the UI?
  4. Do I transfer them as a KeyValuePair or Tuple inside the DTO?
  5. Something else?

I have searched intensively but no other question seems to quite address this issue. Greatly appreciate any suggestions!

EDIT: In the UI both values and units could get changed and sent back to the domain to update.

like image 449
Stefan de Kok Avatar asked Sep 29 '22 16:09

Stefan de Kok


2 Answers

I would be inclined to agree with debuggr's comment above if these are one-way transfers; Value Objects aren't really Domain objects - they have no behaviour that can change their state and therefore in many ways they are only specialised "bit-buckets" in that you can serialise them without losing context.

However; if you have followed DDD practices (or if your back-end is using multi-threading, etc) then your Value Objects are immutable i.e they perhaps look something like this:

public class Money
{
    readonly decimal _amount;
    readonly string _currency;
    public decimal Amount {get{return _amount;}}
    public decimal Currency {get{return _currency;}}

    public Money(decimal amount, string currency)
    {
        //validity checks here and then
        _amount=amount;
        _currency=currency;
    }
}

Now if you need to send these back from the client, you can't easily re-use them directly in DTO objects unless whatever DTO mapping system you have (custom WebAPI Model binder, Automapper, etc) can easily let you bind the DTO to a Value Object using constructors...which may or may not be a problem for you, it could get messy :)

  • I would tend to stay away from "generic" DTO objects for things like this though, bear in mind that on the UI you still want some semblance of the "Domain" for the client-side code to work with (regardless of if that's Javascript on a Web Page or C# on a Form/Console, or whatever). Plus, it tends to be only a matter of time before you find an exceptional Value Object that has Name/Value/Unit/Plus One Weird Property specific to that Value concept

  • The only "fool-proof"*** way of handling this is one DTO per Value Object; although this is extra work you can't really go wrong - if you have lots and lots of these Value Objects, you can always write a simple DTO generation tool or use a T4 template to generate them for you, based on the public properties of your Value Objects.

***not a guarantee

like image 166
Stephen Byrne Avatar answered Oct 10 '22 13:10

Stephen Byrne


DDD is all about behavior and explicitly expressing intent, next to clearly identifying the bounded contexts (transactional and organizational boundaries) for the problem you are trying to solve. This is far more important than the type of "structural" questions for which you are requesting answers.

I.e. starting from the "Domain Entities" that may have "Value Objects", where "Domain Entities" are mapped as a "DTO" to show/be edited in a UI is a statement about how you have structured things, that says nothing about what a user is trying to achieve in this UI, nor what the organization is required to do in response to this (i.e. the real business rules, such as awarding discounts, changing a shipping address, recommending other products a user might be interested in, changing a billing currency, etc).

It appears from your description, that you have a domain model that is mirroring what needs to be viewed/edited on a UI. That is kinda "putting the horse behind the carriage". Now you have a lot of "tiers" that provide no added value, and add a lot of complexity.

Let me try to explain what I mean, using the (simplified) example that was mentioned on having an "Order" with "Money". Using the approach that was mentioned, trying to show this on screen would likely involve the following steps:

  1. Read the "Order Entity" for a given OrderId and its related "Money" values (likely in Order Lines for specific Product Types with a given Quantity and Unit Price). This would require a SQL statement with several joins (if using a SQL DB).
  2. Map each of these somehow to a mirroring "domain objects" structure.
  3. Map these again to mirroring a "DTO" object hierarchy.
  4. Map these "DTO" objects to "View" or "ViewModel" objects in the UI.

That is a lot of work that in this example has not yielded any benefit of having a model which is supposed to capture and execute business logic.

Now as the next step, the user is editing fields in a UI. And you somehow have to marshal this back to your domain entity using the reverse route and try to infer the user's intent from the fields that were changed and subsequently apply business rules to that.

So say for instance that the user changes the currency on the "MoneyDTO" of a line item. What could be the user's intent? Make this the new Billing Currency and change it for all other line items as well? And how does this relate to the business rules? Do you need to look up the exchange rate and change the "Moneys" for all line items? Is there different business logic for more volatile currencies? Do you need to switch to new rules regarding VAT?

Those are the types of questions that seem to be more relevant for your domain, and would likely lead to a structure of domain entities and services that is different from the model which is viewed/modified on a UI.

Why not simply store the viewmodel in your database (e.g. as Json so it can be retrieved with a single query and rendered directly), so that you do not need additional translation layers to show it to a user. Also, why not structure your UI to reveal intent, and map this to commands to be sent to your domain service. E.g. a "change shipping address" command is likely relevant in the "shipping" bounded context of your organisation, "change billing currency" is relevant in the "billing" bounded context.

Also, if you complement this with domain events that are generated from your domain, denoting something that "has happened" you get additional benefits. For example the "order line added" event could be picked up by the "Additional Products A User Might Be Interested In" service, that in response updates the "Suggested Products" viewmodel in the UI for the user.

I would recommend you to have a look at concepts from CQRS as one possible means for dealing with these types of problems. As a very basic introduction with some more detailed references you could check out Martin Fowler's take on this: http://martinfowler.com/bliki/CQRS.html

like image 32
Alex Avatar answered Oct 10 '22 15:10

Alex