0

I am designing a guest check-in system for a residency hall. Residents can check-in guests, but only if these two constraints are met. 1. A resident can only have 2 guests checked-in at once 2. A guest can only be checked in with one resident

The check-in process results in a Visit.

I'm having trouble figuring out where these rules should be implemented. I started with this

var visit = resident.checkin(guest);

But that means I am modifying (or creating) three aggregates in one action:

  • Resident (increment # of checked in guests)
  • Guest (set them as checked in)
  • Visit = created

I don't see a concept in the domain to model as aggregate to hold these rules. Residents and guests exist outside of visits (or across other visits), so can't be wrapped in another aggregate.

I thought about a Saga, but that ends up in steps that don't make sense in the domain (like checking in the student and guest separately to see if one fails).

I could use some guidance? Is my modelling just off?

Luke Bailey
  • 129
  • 1
  • 4
  • If you want simple strong consistency you could express the rule 2 with a DB unique constraint perhaps? Otherwise it would either need to be done through a saga or allow one rule to be broken and generate exception reports. The rule could only be broken through concurrency anyway and how likely would it be that two different people try to register as the same guest at the very same time? – plalx Nov 16 '18 at 14:07
  • @plalx I don't see this as a good solution as it is a **Domain Rule but living in the Infrastructure layer** - i.e. it turns DDD on the top of its head. Otherwise a simple approach but with some architectural consequences – Borislav Sabev Nov 19 '18 at 12:01
  • Possible duplicate of [How to handle set based consistency validation in CQRS?](https://stackoverflow.com/questions/2916899/how-to-handle-set-based-consistency-validation-in-cqrs) – guillaume31 Nov 19 '18 at 12:56
  • 1
    @BorislavSabev Perhaps, but it's not a very interesting rule. A unique constraint is a pragmatic solution in this case IMO. It's not a distributed system and there's no need to overcomplicate things. – plalx Nov 19 '18 at 15:59

3 Answers3

0

In my mind the checked in state of the Resident and Guest should be viewed as a part of a 3rd AR and the incremental field in the Resident should just be incremented when a Visit is confirmed.

Approach 1:

I don't see a concept in the domain to model as aggregate to hold these rules. Residents and guests exist outside of visits (or across other visits), so can't be wrapped in another aggregate.

This does not mean that your Visit cannot hold references to the Resident and Guest. If this 3rd aggregate is the Visit and it has states (for e.g. pending, confirmed, etc.) you can validate the state in a synchronous way and go through the states as the rule commands.

Approach 2 - Use Events for this:

There many ways to integrate events architecturally. Event Sourcing and CQRS could be a good fit here as it will change the way that you think of ARs, Entities and especially state changes.

Short CQRS definition:

Capture all changes to an application state as a sequence of events

Whatever you choose between 1 and 2 you should think of the Visit validation as a separate thing. The Resident should not validate how many guests there are at once, the Guest should not validate if 'he' is at two places at once. Those are all Business Rules related to Visits.

Some links: I've recently answered a DDD question in a similar domain CQRS's "official site" by Greg Young Docs on CQRS by Greg Young Martin Fowler's notes on Event Sourcing When to avoid CQRS? Tons of videos and presentations on the subject

Naeem Sarfraz
  • 7,360
  • 5
  • 37
  • 63
Borislav Sabev
  • 4,776
  • 1
  • 24
  • 30
  • 1
    The first approach breaks the very concept of ARs. An AR shouldn't be part of a larger one. If you really want to modify many ARs in a single transaction then using a domain service that modifies `Resident`, `Guest` and creates `Visit` would be the way to go: should you want to implement eventual consistency later you wouldn't have to change the AR boundaries and it's also much more intuitive. As for implementing eventual consistency with events your answer is way too generic... – plalx Nov 19 '18 at 15:57
  • @plalx you are right. I was talking about references and some external entity that modifies them. Indeed this is Service functionality – Borislav Sabev Nov 20 '18 at 14:21
0

Creating a new aggregate B as part of existing aggregate A's transaction is usually not a problem in terms of concurrent access and consistency (since no one knows B yet). Here you have an additional invariant on the number of Visits which could be an exception to that, but it's enforced in Resident and Resident is the only entry point to creating a Visit, so everything boils down to a modification of Resident which is safe concurrency wise.

Then you have a global constraint on how many visits a guest can be in at a time. The issue has been answered multiple times on SO, but I would first of all recommend getting back to your domain experts to discuss the rule and possible variations of it, because there's a world of possibilities (eventual consistency, manual intervention, etc.) between "the constraint must be enforced and immediately consistent" and "the rule is useless".

guillaume31
  • 13,738
  • 1
  • 32
  • 51
0

Residence Hall - Aggregate Root (AR)

Guest - entity

Resident - entity

Visits - Value Object (list of Visit)

Commands for AR:

  1. CheckInGuestCmd(guest,resident)
  2. CheckOutGuestCmd(guest)

First command will check visits and enforce invariants than add visit

Second command will remove existing visit if it exists

aelor
  • 60
  • 5