jdutheil / booking-ddd

5 stars 1 forks source link

CRUD detected! #4

Closed tclasen closed 7 months ago

tclasen commented 8 months ago

https://github.com/jddw-dev/booking-ddd/blob/f2d6694ee69d5fec343ca45c98130f9fcd8af40c/src/libs/ddd/repository.port.ts#L26

Repositories shouldn't be generic, period. If you want to avoid CRUD and follow DDD then you don't want to define your repositories in the generic layers, and instead create 1 repository per aggregate in your system. Every method on said repos should should only take either domain models or fields of domain models as input and return domain models or fields of domain models as output. Additionally, all methods on these domain-specific repos should not be generic CRUD operations but instead map cleanly to your use-cases. Such as a new method called AllMusiciansBookedDuring(StartDate, EndDate) or a AllBookingsFor(MusicianID). These read like something a booker would said to their administrative clerk when requesting data to make decisions on. "Hey James, can you get me a list of all booked musicians for the second half of March?".

While sometimes these end up with exactly the same CRUD implementations inside the methods, the purpose of them are super specific and allow you to couple to them safely. Sometimes you end up with multiple methods with exactly copy / paste of the implementation details in the same repo, and that is fine. This allows them to evolve independantly as needed without breaking other parts of the code due to the previously mentioned coupling.

jdutheil commented 8 months ago

This one hits me hard, it's like I suddently understand all the stuff I was seeing related to "DDD is not CRUD". I naively thought "what's wrong with some common methods ?", but now it makes more sense, thanks to you ! One thing where I'm still a little confused ; let's says I have a Contact aggregate, with multiples properties (adress / email / phone, for example). Those properties will probably be updated together, how should I approach that ? (Maybe I'm still thinking too much in a CRUD-way) Should I have methods for each one in my repository (updateAddress, updateEmail, updatePhone) and call them one by one, or would it be fine to have a method like updateCommonProperties (more specified, ofc) that might update each of these properties together ?

tclasen commented 8 months ago

An aggregate represents a consistency boundary. So if you have a complex aggregate with a complex graph of child entities and value objects you would still have a single save method on the repo that saves all of it in a single transaction. Having a method that takes in the aggregate and saves it as is is common, and having a fetch one by id method is common. It is really the complex queries on the read side of the CQRS that read like prose, and some people like to split them into different repos. The write path is simple, read one by id then write one by passing in the whole aggregate.