Domain-driven design: some notes from DevoxxFR 2024

Liping Mechling
Publicis Sapient France
3 min readMay 7, 2024

Recently I attended the Devoxx in Paris, I want to share what I’ve learned from the talk (in French on YouTube now when I edit this article): “Model Mitosis : ne plus se tromper entre les microservices et le monolithe”, which will be translated as “Model Mitosis: Don’t make a mistake between microservices and monoliths anymore”, by Julien Topçu and Josian Chevalier from Shodo.

Domain-driven design

Imagine we have to design an airplane booking system, we can start by observing the client's behavior first, the client would want to search, select, and then book, so we have at least Search and Booking in the domain.

At the beginning, we had a monolith architecture application, that contained Search, Booking, and a SharedKernel, they are all in the same domain, too many responsibilities for a single domain.

When we separate these domains, we found that the Booking and Search’s lifecycle is tightly bounded, Search needs a list of SpaceTrains to search, then Booking needs only one Fare (named Price later) to book.

SpaceTrain in Booking is created to answer the Search problem, but it’s used also to answer the Booking problem.

Problem 1:

Dependency problem, Booking needs Search to provide a selected SpaceTrain so it can complete the job of booking. So the code just imported Search into Booking, which is coupled.

Solution 1:

Introduce in Booking a searchId and a service called RetrieveSearchService, which are included in the SPI (Service Provider Interface), and will be implemented by an antiCorruptionLayer outside of the two services, so no need to import Search anymore. It’s called dependency injection.

Problem 2:

The client wants to choose a seat in either first class or second class, for different levels of comfort in these classes that the SpaceTrain can provide.

SeatLocation is in Booking, but Search needs to see if the seat is compatible with the client’s choice or not.

There is a Fare in SharedKernel. It calculates the price with a taxRate, which is retrieved from Booking, so the two are coupled.

Solution 2:

Copy Fare and ConfortClass to both Search and Booking.

Rename Fare to Price in both of them.

Make it the Booking’s responsibility to calculate the price.

Problem 3:

The Schedule is broken because “Departure cannot be in the past”.

New booking departure time depends on the actual booking status.

Solution 3:

Duplicate Schedule class into both Booking and Search.

The booking domain should check that the departure is not in the past for any new booking, and the previous booking is finished.

Schedule in Booking only checks if the arrival is after departure, while in Search it checks both if departure is after now and arrival is after departure.

Finally, in SharedKernel we have only the Amount and Currency, let’s rename it as Money repository, which will serve as a generic subdomain, used by both Search and Booking.

In conclusion, sometimes decoupling can be done by duplicating the models, or in more sophisticated cases, by specializing/customizing the model in each service.

How to find the boundaries? What is a SharedKernel? What should be put in and what should not? Everything in the SharedKernel must have the same meaning, the same presentation (structure and type), the same invariants, and the same business rules, for all the bounded contexts that use it, all the others should be separated.

And between teams, the SharedKernel should not be shared.

So that’s all I’ve learned. Here is the source code in GitLab: Model Mitosis.

Seems easy, right? Thanks for reading!

--

--