1

I am new to DDD approach and need some advice.

I am going to use event site as an example. Say user can create event with start/end date. Event status can be running, expired, closed. Closed status can only be assigned by user manually after creating event. The other two status are calculated on the fly by checking the date. A closed status trumps the other two status.

Now. When creating event, No status needs to be passed, just the date info is sufficient. But when fetching an event, status needs to be fetched and calculated based on date when status is not in closed status.

So status has different requirements based on create and get operations. My current approach is to pass status to Event constructor (null for create). To put in pseudo code:

# creating a event
status = null
Event = new Event(uid, status, desc, date_info)
# get a event in repo
date_info = [start_date => repo->start_date, end_date => repo->end_date, date_type => repo->date_type]
Event = new Event(repo->uid, repo->status, repo->desc, repo->date_info)
# then in event entity, I do

If event status is null,
  Status = Calculate status by date
If event status is closed,
  Status = closed

I felt a bit dirty by the way of status passed to event constructor because I am depending on the caller. And I can’t stop it passing closed status during creation.

Is there a better way to do it or I am thinking too much?

update

To be clear, status is the value object that I am refering to in this question. Event is the entity.

BTW, You can think of the event example is like meetup.com

Like meeting can be cancelled, Event organizer may need to close the event manually due to space issue, budget shortage etc.

  • You could create RunningEvent, ExpiredEvent and ClosedEvent classes with the same interface. Then you can give each class it’s own unique behavior without checking for status. This is called polymorphism. Also read up on value objects, because your event classes aren’t. – Rik D Oct 8 '19 at 15:26
  • 1
    Strange advice about having different classes for basically the same entity: Are events somehow "rehydrated" into different classes automatically or what is the point? From the question, Event is not good example of a value object, true. – Roman Susi Oct 8 '19 at 16:33
  • @Roman Susi it’s not that weird imo. I doubt a ‘status’ property is part of the ubiquitous language of the event domain and in general status properties lead to unnecessary if/case statements. Take the Register method for an event, the behavior depends on the status of the event; I prefer to solve that with polymorphism rather than property checking. I know it’s a fake example, but it’s probably worth it to dive a little bit deeper into the domain and you might find much nicer and cleaner solutions. – Rik D Oct 8 '19 at 18:32
  • It's probably fine in case of transient value objects, and ORMs will happily help with automating polymorphism, but how you imaging practicalities of an entity class changing with time? A second ago it was FutureEvent and now it's PastEvent (with the same identity)? (I am just curious as I also like polymorphic approaches.) – Roman Susi Oct 8 '19 at 19:42
  • @RikD Thanks for interesting idea on time-dependent polymorphic discriminator. I've incorporated it in my answer. – Roman Susi Oct 8 '19 at 20:06
1

Like meeting can be cancelled, Event organizer may need to close the event manually due to space issue, budget shortage etc.

This is the key information here. An event is not closed. It can be cancelled.

A budget issue could legitimately cause the event organizer to cancel the event. We had money. Now we don't. Event cancelled.

A "space issue" would not necessarily cancel an event, but might reduce the maximum number of participants for that event. Furthermore, once you reach the max participants, the event is no longer open for registration, so registration for the event can be "open" or "closed". The logic for registering participants could be offloaded to another class, maybe EventSchedule or EventRegistrar.

The Event class could have a method called cancel(string reason) which cancels the event and returns an EventCancellation object recording the reason, and date/time of the cancellation. If participants need to be notified, then either handle that by pushing the EventCancellation object on to a message queue so something else can handle this, or delegate this behavior to the EventRegistrar class.

The status of an event sounds like a separate concept from the fact the event is "currently running" or "expired." An event can be in the "cancelled" or "not cancelled" states. The fact the event is running or expired sounds more like search criteria to me. Any behavior dependent upon the event being "running" or "expired" should be based on the current date and time compared to the event dates, not a status.

That brings us to which constructors the Event class needs:

registrar = new EventRegistrar(max_capacity)
new_event = new Event(event_uid, description, date_info, registrar)

An event fetched from the database would be initialized the same way, except for the registrar maybe:

participants = new EventParticipantsCollection(event_uid)
// One or more lines of code to add participants fetched from the database

registrar = new EventRegistrar(max_capacity, participants)
persisted_event = new Event(event_uid, description, date_info, registrar)

And finally a "cancelled" event looks pretty similar, except it has the event cancellation:

participants = new EventParticipantsCollection(event_uid)
// One or more lines of code to add participants fetched from the database

registrar = new EventRegistrar(max_capacity, participants)
event_cancellation = new EventCancellation(event_uid, cancellation_date, cancellation_reason)
cancelled_event = new Event(event_uid, description, date_info, registrar, event_cancellation)

The constructor signatures clearly indicate what state the event is currently in:

new_event       = new Event(event_uid, description, date_info, registrar)
persisted_event = new Event(event_uid, description, date_info, registrar)
cancelled_event = new Event(event_uid, description, date_info, registrar, event_cancellation)

You can see the only difference between a "new" event and an event fetched from the database is the source of the data used to create the object (user interface versus database). The same constructor for a new event is used for a persisted event. This makes sense to me, since "persisting" is a matter for the infrastructure of the application, and not a core domain concept.

The cancelled event is where you see a difference in object initialization. The constructor for a cancelled event takes an event_cancellation object as a parameter. An event being cancelled is a domain concept and therefore deserves its own treatment in the domain model.

  • This is very good answer with an extensive example of domain analysis. While the OP may have "obfuscated" his real need behind "event", this answer illustrates possible way of thinking and logic and will certainly help to solve the concrete problem. – Roman Susi Oct 12 '19 at 10:58
  • Any behavior dependent upon the event being "running" or "expired" should be based on the current date and time compared to the event dates, not a status. What's wrong with having get_status() returns running/not_started/closed? To me, they all sounded like a status. – perlwle Oct 22 '19 at 7:40
0

"So status has different requirements based on create and get operations." Well, status does not need to be one or another, it can be a complex type, which you always give to the constructor, and which can have "slots" for all parts (some parts none/null). You can probably even make a separate event status class if your domain in question suggests that. Then you will effectively abstract away the implementation details (no problem with ifs in there) and expose only a method for the resulting status for other domain objects.

In DDD it is important that your Domain layer is as pure as possible, so code reads as prose and can be almost understandable to less technical stakeholders. If event status calculation is not part of the domain - put it into application layer or persistence layer or what is appropriate (you know you case best).

If it's internals are part of the domain, then domain logic will tell you whether Event changes class (like Baby becomes Toddler, Toddler becomes Preschool, ..., etc.) or it's just a property. In case of property there is nothing wrong to use simple conditional statements for rules or (in more advanced cases) finite-state machine or whatever suits the domain best.

That is why it is domain-driven design: domain informs your decisions: Object/entity does not belong to domain, or it does and you can see it's role and contents there: From the language of domain experts.

Also you probably forgot the transition between states. Do experts speak about something special when event changes state? Is the whole system notified about that? Etc.

Domain-driven design is there to think about essentials, not every constructor out there. It may even be Domain Model is not the best choice for your particular case, and, lets say, Active Record is a better one.

  • Also you probably forgot the transition between states. Do experts speak about something special when event changes state? Is the whole system notified about that? Etc. yes, It is handled by event such as when user changes status to closed. I left it out because it's irrelevent to my question. – perlwle Oct 9 '19 at 7:02
  • 1
    @perlwle: How events transition between states, and why they are transitioned is the key missing information. There's been lots of chatter about this question with few answers, and I think more info about why a user "closes" an event, and what happens in the larger system as a result would help. – Greg Burghardt Oct 9 '19 at 11:05
  • Ideally value object should be non mutable. (We should not be able to change the properties of the value object after creating it). The value objects will not live by itself, it will be associated with an aggregate. May be you could explain the situation little more. Explaining the use case a bit more along with the associated entities could help. – Nachiappan Kumarappan Oct 9 '19 at 18:06
  • @GregBurghardt Like meeting can be cancelled, Event organizer may need to close the event manually due to space issue, budget shortage etc. – perlwle Oct 10 '19 at 2:50
  • @perlwle: A cancelled meeting sounds like a status. An event being closed does not sound like a status. It sounds like you cannot add participants if there is not enough space (an EventScheduler or EventSchedule class maybe?). A budget issue might cause an event to be "cancelled", or it might cause the max capacity for the event to be reduced (back to EventSchedule again). I think you might be trying to model the wrong things here. – Greg Burghardt Oct 10 '19 at 10:44

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.