Communicating between bounded contexts

984 views
Skip to first unread message

Martin Nilsson

unread,
Mar 7, 2011, 2:13:31 AM3/7/11
to ddd...@googlegroups.com
How do you communicate between bounded contexts when using CQRS? In "normal" DDD I would use an application service from another app service.

For example, I have this, simplified, model:

HotelSetup BC:
  Hotel
    0..* RoomType
      0..* Room
     
RoomBooking BC:
  BookableHotel
    0..* BookableRoomType
      1..* BookableRoom
     
HotelSetup will handle such things as address, employees, cleaning of rooms, etc
RoomBooking will handle bookings, checkins, checkouts, etc

In RoomBooking I have a query which finds available rooms for booking by taking something like number of rooms - room bookings for a specific period.

Would you suggest to duplicate the room types (a subset, only bookable) in the hotel into RoomBooking or should I expose queries from HotelSetup which then RoomBooking could ask for number of rooms in a room type?
When command BookRoomTypeCommand is arriving I probably also want to do some validation that there are enough rooms available.
Maybe I have got my BC's wrong?

@seagile

unread,
Mar 7, 2011, 4:19:53 AM3/7/11
to DDD/CQRS
Using events this might be trivial.

Martin Nilsson

unread,
Mar 7, 2011, 5:33:56 AM3/7/11
to ddd...@googlegroups.com
Do you handle them in sagas then?
HotelSetup.RoomRegisteredEvent => RoomBooking.CreateBookableRoomCommand

Andreas Öhlund

unread,
Mar 7, 2011, 5:35:57 AM3/7/11
to ddd...@googlegroups.com
>How do you communicate between bounded contexts when using CQRS? In "normal" DDD I would use an application service from another app service.
You only apply CQRS within a bounded context so I don't think that is what causing your "communication needs".  Cross BC queries is usually a sign that the responsibilities of your BC's needs to be adjusted.


>RoomBooking will handle bookings, checkins, checkouts, etc
Why would booking be concerned about checkins and checkouts?

> 1..* BookableRoom
Is each individual room really nessesary for doing a reservation?

>HotelSetup 
Wouldn't this BC be interested in who is checking in and who's is checking out? (you mention that this BC is responsible for the cleaning of the rooms)

Perhaps "HotelManagement" or "Facilities" would be a better name?


>RoomBooking could ask for number of rooms in a room type
Couldn't this be modeled as two different concepts? ( HotelSetup is master for the "capacity" of the hotel and Booking is master for "availablity")


>When command BookRoomTypeCommand is arriving I probably also want to do some validation that there are enough rooms available.
Validating availability should be easy since it's owned by the booking BC.

Is the command really invalid if there is no availability? (hint: overbooking policies)

Cheers,

Andreas

http://andreasohlund.net
http://twitter.com/andreasohlund





Date: Mon, 7 Mar 2011 08:13:31 +0100
Subject: [DDD/CQRS] Communicating between bounded contexts
From: mffm...@gmail.com
To: ddd...@googlegroups.com

Nuno Lopes

unread,
Mar 7, 2011, 7:30:32 AM3/7/11
to ddd...@googlegroups.com
Hi,

> How do you communicate between bounded contexts when using CQRS? In "normal" DDD I would use an application service from another app service.

You can still use an app service. The service defines the commands and domain events within the scope of an activity within a BC.

> Would you suggest to duplicate the room types (a subset, only bookable) in the hotel into RoomBooking or should I expose queries from HotelSetup which then RoomBooking could ask for number of rooms in a room type?

You need to analyse if the team that manages Bookable rooms (defines bookable rooms and closes rooms for booking) are distinct from the people that manages bookings. Most probably are since hotel inventory and maintenance is managed differently.

So if the HotelSetup defines what is bookable or not then you don't need to replicate the entire Bookable inventory. Instead you can use a shared view.

The RoomBooking BC can simply publish events of what is booked (RoomBooked). The Hotel Setup publishes events of what is available to be booked (BookableRoomDefined, RoomClosedForBooking). The View denormalizes both data streams into one shared view.

If your BCs are well defined this should be enough.

Nuno

Nuno Lopes

unread,
Mar 7, 2011, 7:31:36 AM3/7/11
to ddd...@googlegroups.com

HotelSetup.RoomRegisteredEvent => RoomBooking.CreateBookableRoomCommand

Why do you think you need Sagas for this?

Nuno

Martin Nilsson

unread,
Mar 7, 2011, 10:47:10 AM3/7/11
to ddd...@googlegroups.com
Thanks for your comments Andreas. My comments inline.

On Mon, Mar 7, 2011 at 11:35 AM, Andreas Öhlund <andreas...@hotmail.com> wrote:
>How do you communicate between bounded contexts when using CQRS? In "normal" DDD I would use an application service from another app service.
You only apply CQRS within a bounded context so I don't think that is what causing your "communication needs".  Cross BC queries is usually a sign that the responsibilities of your BC's needs to be adjusted.

But somehow the information must come to the other BC and do you do that by eventhandlers or something else? Do you have other ways of doing this when creating and maintaining another BC's *domain model* comparing to maintaining a read store? Do you have a eventhandler that do something like this then:

RoomBookingDomainModelEventHandler : IEvenHandler<HotelSetup.RoomRegisteredEvent>  
    Handle(HotelSetup.RoomRegisteredEvent evnt)
         if(RoomIsBookable(evnt))
             Execute CreateBookableRoomCommand(evnt)
 

>RoomBooking will handle bookings, checkins, checkouts, etc
Why would booking be concerned about checkins and checkouts?

You check-in on a room booking but maybe you're right that those are separate BC. Actually it was a made up example because I haven't done those stories. This post was more on how to handle the communication.
 

> 1..* BookableRoom
Is each individual room really nessesary for doing a reservation?

>HotelSetup 
Wouldn't this BC be interested in who is checking in and who's is checking out? (you mention that this BC is responsible for the cleaning of the rooms)
The cleaner is only interested in cleaning a room. Maybe they don't care about what kind of room (guest room, reception, etc) it is.
 

Perhaps "HotelManagement" or "Facilities" would be a better name?
Agree
 

>RoomBooking could ask for number of rooms in a room type
Couldn't this be modeled as two different concepts? ( HotelSetup is master for the "capacity" of the hotel and Booking is master for "availablity")

Not sure I follow here. I think it is what we have done.
 

>When command BookRoomTypeCommand is arriving I probably also want to do some validation that there are enough rooms available.
Validating availability should be easy since it's owned by the booking BC.

Is the command really invalid if there is no availability? (hint: overbooking policies)

Yes, we have overbooking policies but it doesn't change the flow. We still have to look for if the room is overbooked.

Nuno Lopes

unread,
Mar 7, 2011, 11:58:24 AM3/7/11
to ddd...@googlegroups.com
Incurring the risk of talking with myself inline with my suggestion:

BookRoomHandler : ICommandHandler<BookRoom>
Handle(BookRoom cmd) {
Booking aBooking = new aBooking(cmd.BookingID, cmd.RoomType, cmd.BookingPlanId , cmd.StartDate, cmdEndDate, cmd.CustomerName, ....) // deferred publishing of RoomBooked event
AllBooking.Save(aBooking) // publishes all events deferred ex: RoomBooked
}


// the view denormalizes the RoomBooked event to the shared view.

Meanwhile:

RoomBookedHandler : IEventHandler<RoomBooked>
Handle(RoomBooked evt) {
BookingPlan aBookingPlan;
if (evt.BookingPlanId == null) {
aBookingPlan = Booking.createBookingPlanForRoomBooked(evt);
}
else 
{
AllBookingPlans aBookingPlan = AllBookingPlans.get(evt.BookingPlanId);
aBookingPlan.AddRoomBooked(evt) // Deferred publishing of RoomOverBooked event if that is the case
}

AllBookingPlans.Save(aBookingPlan) // publishes all events deferred ex: RoomOverBooked
}

// the view denormalizes the RoomOverBooked event to the shared view

Cheers,

Nuno

Nuno Lopes

unread,
Mar 7, 2011, 1:05:36 PM3/7/11
to ddd...@googlegroups.com
Again for checking checkouts:

You check-in on a room booking but maybe you're right that those are separate BC. Actually it was a made up example because I haven't done those stories. This post was more on how to handle the communication.

Processing Checkins Checkout

CustomerCheckinHandler : ICommandHandler<CheckOutRoom>
Handle(BookRoom cmd) {
CheckinRoom aCheckin = new Checkin(cmd.CheckinId, cmd.BookingId) // deferred publishing of CustomCheckedIn event
AllCheckIns.Save(aCheckin) // publishes all events deferred ex: CustomerCheckedIn
}


CustomerCheckOutHandler : ICommandHandler<CheckOutRoom>
Handle(BookRoom cmd) {
CheckOutRoom aCheckOut = new CheckOut(cmd.CheckOutId, cmd.CustomerServiceId) // deferred publishing of CustomCheckedOut event
AllCheckOuts.Save(aCheckin) // publishes all events deferred ex: CustomerCheckedOut
}

// again the view handles it.

Meanwhile:

CustomerCheckInHandler : IEventHandler<CustomerCheckedIn>
Handle(CustomerCheckedIn evt) {
CustomerService aCustomerService = CustomerServiceMgmt.CreateCustomerService(evt); // Publishes CustomerServiceStarted. A lonf running customer service.
AllCustomerServices.Save(aCustomerService);
}

CustomerCheckedOutHandler : IEventHandler<CustomerCheckedOut>
Handle(CustomerCheckedin evt) {
CustomerService aCustomerService = AllCustomerServices.get(evt.CustomerServiceId);
aCustomerService.addCustomerCheckedOut(evt); // publishes CustomServiceEnded (might then be handled by billing)
AllCustomerServices.Save(aCustomerService);
}

Handling room services requests:

RequestRoomServiceHandler : ICommandHandler<RoomServiceRequest>
Handle(BookRoom cmd) {
RoomServiceRequest aRequest = new RoomServiceRequest(cmd.RoomServiceId) // Publishes RoomServiceRequested
AllRoomServicesRequests.Save(aBooking); 
}

// again the view handles it. Meanwhile

ServiceRequestedHandler : IEventHandler<RoomServiceRequested>
Handle(RoomServiceRequested evt) {
RoomService aRoomService = RoomService.get(evt.RoomServiceId);
aRoomService.add(evt); // publishes RoomServiceRequestedAndPending
AllRoomServices.Save(aRoomService);
}

This applies CQRS + Event Sourcing in a full EDA without a Busl.

Notice this assumes that you are using standard services (say WebServices), and a mechanism to dispatch events to subscribers. Not necessarily a messaging bus. For instance publishing an event might mean calling a service on some other BC whose arg is the event

You can short cut some implementation points still is more or less like this. You even collect for instance facts that might be out of spec but that most probably aren't (one way commands remember?). The reason to do so is that if eventually due to nonfunctional requirements handling events fails you can still republish them.. For instance an WebService might not be available.

Does this help?

Nuno


Martin Nilsson

unread,
Mar 7, 2011, 4:30:39 PM3/7/11
to ddd...@googlegroups.com
So what you are saying is that we are building other BC's domain models by event handlers, just as we are doing with read store. Seems logical to me.

Andreas Öhlund

unread,
Mar 8, 2011, 3:11:14 AM3/8/11
to ddd...@googlegroups.com
>But somehow the information must come to the other BC and do you do that by eventhandlers or something else?
The only data that is duplicated between BC's is ID's and those are shared using an event. So if we agree that facilities manage capacity and Booking manage availability this would lead us into something like this:


Facilities publish: (when capacity changes, new rooms built, rooms closed for renovation etc)

CapacityChangedForRoomType
    RoomTypeId
    Delta
    Period


Booking receives the event and stores the availability:

Day, RoomType,NumAvailableRooms

2011-04-01, 45(Superior), 56


When a customer searches for availability the Booking BC query it's local data store (CQRS might be used) and finds the number of available rooms (no cross domain query)

When a command to book a room arrives Booking again looks at its local store and determine if the booking should be accepted based on the current availability and the overbooking policy for that room type


>The cleaner is only interested in cleaning a room. Maybe they don't care about what kind of room (guest room, reception, etc) it is.
True but the room number is interesting to her ( perhaps the room type as well, certain room types should perhaps have fresh flowers etc etc)


>Yes, we have overbooking policies but it doesn't change the flow. We still have to look for if the room is overbooked. 
Isn't the room type enough?

Cheers,

Andreas

Date: Mon, 7 Mar 2011 16:47:10 +0100
Subject: Re: [DDD/CQRS] Communicating between bounded contexts

Nuno Lopes

unread,
Mar 8, 2011, 7:19:19 AM3/8/11
to ddd...@googlegroups.com
You can use events to feed other BCs with facts occurring in the BC. Other BCs handle them as they see fit. 

When a CostumerCheckedIn different BCs can do different things. 

Sent from my iPad
Reply all
Reply to author
Forward
0 new messages