Update DDD via REST

I'm new to DDD, and I'm trying to figure out a way to update an aggregate using the verb PUT.

If all the properties in the aggregate have private setters, then it is obvious that I need to have a set of functions for each business requirement. For example

supportTicket.Resolve(); 

It is clear to me that I can achieve this with an endpoint such as /api/tickets/5/resolve , but what if I want to provide a way to automatically update the entire ticket?

As an example, the user can make a PUT request for /api/tickets/5 with the following body

 {"status" : "RESOLVED", "Title":"Some crazy title"} 

I need to do something like this in ApplicationSercvice

 if(DTO.Status != null && dto.Status == "RESOLVED") supportTicket.Resolve(); if(DTO.Title != null) supportTicket.setNewTitle(DTO.title); 

If in this case there is some business logic to change the name of the ticket in order to prevent it from changing, if the ticket is allowed, should I consider some priorities when updating the unit, or am I considering this completely wrong?

+7
c # domain-driven-design asp.net-web-api2
source share
4 answers

Domain Managed Project for RESTful Systems - Jim Webber

What if I want to provide a way to update an entire ticket atomically?

If you want to update the entire ticket atomically, precise aggregates; aggregates are the wrong tool in your field if what you really want is a repository of key values ​​with CRUD semantics.

Aggregates only make sense when their domain business rules apply. Do not build a tractor when all you need is a shovel.

As an example, a user can make a PUT request for / api / tickets / 5

It will be a mess. In a CRUD implementation, replacing the current state of a resource by sending it a representation of a new state is appropriate. But this is not at all suitable for aggregates in general, because the state of the aggregate is not under the control of you, the client / publisher.

A more suitable idiom is to publish a message on the bus that, when processed by the domain, will have a side effect of achieving the desired changes.

 PUT /api/tickets/5/messages/{messageId} 

NOW your application service is viewing the message and sending commands to the aggregate

 if(DTO.Status != null && dto.Status == "RESOLVED") supportTicket.Resolve(); if(DTO.Title != null) supportTicket.setNewTitle(DTO.title); 

This is normal, but in practice it is much more common to make the message explicit about what needs to be done.

 { "messageType" : "ResolveWithNewTitle" , "status" : "RESOLVED" , "Title":"Some crazy title" } 

or even...

 [ { "messageType" : "ChangeTitle" , "Title" : "Some crazy title" } , { "messageType" : "ResolveTicket" } ] 

Basically, you want to provide the application with enough context so that it can perform a real message check.

let's say I had aggregates that encapsulated the necessary business logic, but there was also a new demand for atomic update functions, and I was trying to figure out the best way to handle this.

So, the right way to deal with this is to first deal with it at the domain level - sit down with experts from your domain, make sure that everyone understands this requirement and how to express it in the ubiquitous language, etc.

Implement any new methods that you need in the consolidated root.

After using the use case in the domain correctly, you can start worrying about your resources following the previous pattern - the resource simply accepts the incoming request and invokes the appropriate commands.

+6
source share

Does the ticket resolution requirement change the name? If not, they should not be the same actions in DDD. You would not want to not allow the ticket if the new name was invalid, and you would not want to not change the name if the ticket was not allowed.

Make 2 calls to perform two separate actions. It also allows flexibility, for example, the name can be changed immediately, but perhaps the "resolution" of the ticket will begin the execution of a complex and time-consuming (asynchronous) workflow before the ticket is actually resolved. Perhaps he should have a manager? You do not want the call to change the "header" associated in this mix.

If necessary, create something to organize several teams according to the comment of @VoiceOfUnreason.

If possible, keep things separate and use code to use cases, rather than minimizing intercations with entities.

+2
source share

You are probably right. But it is probably wiser to encapsulate such logic inside the ticket itself by making the "change ()" method, getting changeCommandModel (or something like that) so that you can define business rules inside your domain object.

0
source share
  if (DTO.Status != null && dto.Status == "RESOLVED") supportTicket.Resolve(DTO.title); 

I will modify the base method to take the header as a parameter, this will clarify the resolution action. This second, if validation you want in the domain method. This is really a preference, the main thing is the message, and I agree with the second option @VoiceOfUnreason.

0
source share

All Articles