1

May I be so bold as to ask (from an amateurish point of view), what is the general strategy for dealing with potentially ever expanding domain models?

By way of example, I have Staff, and in the beginning they could just have have a department (although sadly, in reality, it had to be a List<Department> because it's never that easy, right?) Then we go and do a carpark module, and every staff member could have a List<Car>. Ok, no big deal.

Then I want to introduce a kind of workflow schema. I might end up with a staff member having a whole array of items that they need to deal with. So I add a list to staff List<WorkflowItem>? (Am I on the right track or has something gone wrong already? It already feels weird, but from a database point of view that kinda makes sense - Get all outstanding items for John )

THEN, we're talking about a booking system for parents' evening, where students should book themselves in for a time slot with a staff member. Now I'm starting to worry. Will my staff really have a collection of time slots (probably within a collection of events)? Probably not right?

It seems to make sense that a staff member should have their cars, but not so much their bookings. Is that a legitimate point of angst, or am I missing something glaringly obvious?

The point is, I guess I could add it, but should we just go on adding properties ad infinitum?

3
  • 3
    All sounds legit to me. The world isn't simple. Every business system I've ever worked on has entities numbering in the hundreds. Nov 9, 2018 at 20:01
  • Sure you can model the world - but for what purpose? DDD is not an end in itself, the purpose is to develop some software. So stop modeling whenever you have modeled all the things your team needs to develop software for a certain use case. If you don't know the use case limits (yet), you will be better of stop doing what you are doing now and better invest the time into use case analysis.
    – Doc Brown
    Nov 10, 2018 at 10:13
  • .. I once met a company who tried to model all relevant data for the german health accounting system - several hundred classes with dozens of attributes. Unfortunately, they had no idea about the use cases in that system. Needless to say all their work was finally dumped into the basket.
    – Doc Brown
    Nov 10, 2018 at 10:19

2 Answers 2

8

You certainly can design the model as you propose, but you will eventually end up with a (very) big ball of mud. You are not the first one to encounter this problem, that's why concepts like SOA, microservices and DDD appear.

The idea is that the concerns of your application can be grouped in some way that are relatively independent of each other (services). For example, the fact that a staff member belongs to one or more department, has a car and has a list of workflow items, doesn't mean that you need these concepts in one place to perform the required logic.

Does the car park module need to know about staff members departments? and their workflow items? Probably not. What about the list of cars? probably yes. So your carpark module, might have a list of staff members with their cars, but not with their workflow items.

Note that another service might also have a list of staff members with other properties. These are really not duplicate data, because they basically share the staff member Ids, but not actual data, simply because the data a services uses is irrelevant for another service.

If you follow this approach, you'll see that there isn't a single "Staff member" entity in your application. Every service has "a piece" of staff member, a projection of the whole that is relevant within that context. In DDD, several bounded contexts might contain the same concept, like "order" or "customer", but with a different meaning.

This approach introduces some complexities. For example, eventual consistency. In this setup, one service will be in charge of creating (hiring?) and deleting (terminating the contract) of staff members. When this happens, this service will publish an event like StaffMemberHired or StaffMemberContractTerminated. The other services will listen to these events and act accordingly. For example, the carpark service will create an entry in the car park staff members list. From this moment, the module will allow a user to assign a car and a parking spot to that new staff member. When the carpark module receives the StaffMemberContractTerminated event, it will unassign her parking spots and mark the staff member entry as terminated, so that it won't be possible anymore to assign a parking spot to it. The eventual consistency part implies that there is a possibility that for a short period of time a staff member is created and it's still not available to assign a parking spot, or it's terminated, but the parking spot is still reserved. These situations are resolved in very short periods of time normally.

These concepts are a bit confusing to grasp in the beginning and sometimes a bit hard to apply, but they solve your question: they allow an ever growing domain, because you basically add new services to the system, instead of increasing the size of a single model.

2
  • Ahh, so that's the point of microservices. I hear these terms banded about but they don't take on much meaning until you actually bump up against a case for their use.
    – Mikustykus
    Nov 10, 2018 at 12:17
  • 1
    Exactly. The problem with microservices is that many people misunderstand them and they do a Staff microservice, a Department microservice, etc. and with this approach you end up again with the same problem. The staff microservice will grow endlessly and you'll have a complex network of complex microservices all talking to each other. Instead, if you create microservices focussed on business capabilities (sales, marketing, logistics, billing,... ) their scope and dependencies with each other are limited and manageable. Nov 10, 2018 at 13:19
1

Complicated problems are complicated. There's usually no way to solve that. We can only shuffle the complexity around so that it appears less complicated: divide the complexity along various criteria, or hide some complexity behind an abstraction.

For your domain model, you might notice that different problem domains like “booking time slots” and “managing a fleet of cars” have overlapping entities such as staff members, but are otherwise completely separate. Domain Driven Design suggests that these can be modeled as separate “bounded contexts”.

  • In the time slot booking context we can have a staff entity that is associated with available time slots.
  • In the fleet management context, we can have a separate driver entity that is associated with a number of cars.

Both of these staff/driver models represent the same staff member but are modeled separately – the model is not shared across contexts. Instead, we translate between the models at the context boundary. That lets you avoid the need of one gargantuan Staff class with dozens of properties.

While this makes the model simpler because each individual context is now a lot smaller and unrelated properties are no longer entangled with each other, overall complexity may have increased because we now have to deal with multiple contexts and multiple entities representing the same staff member – we are shuffling complexity around. You have to decide whether this tradeoff is worth it in your case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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