Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Design

Slices

The list in the introduction reveals the use-case slices,
modules expressing the logic for each use-case. Each slice is a
separate project in the sample code.

...

  • Hotel.Base is more or less a naked set of properties
    (with some getters/setters), which form the core domain,
    without implementing any business logic. This is a recommendation
    from Ivar Jacobson.
  • Hotel.Reservations implements the reservation system
    with a mixin to Hotel.Base
  • Hotel.Accommodation contains another mixin to Hotel.Base, but
    it somewhat depends on Hotel.Reservation, because accommodation
    is a special case of reservation
  • Hotel.Discount is a mixin to the accommodation Hotel mixin
    (that's right – a "mixinmixin") that extends the Hotel.Accommodotion
    slice, implemented as a mixin.
  • Hotel.Queue is a slice extending the reservation slice,
    implemented as another mixinmixin. If the hotel is booked to capacity for a given
    week, then guests' reservations are put into a queue

Data is stored in separate repositories, i.e. we use the repository
pattern here. Three repositories exist, each with its own type
of record(s):

  • ReservationInfo – name, room number, week
  • AccommodationInfo (and Bill)
    • AccommodationInfo – name, room number
    • Bill – number of beers, number of sodas
  • QueuedReservationInfo name, week
  • DiscountInfoRepository – for counting past visits

As you see, there is one repository for each slice. If you find
the implementation strange, read this
hand-waving.

Note
titlewe don't store bills

We don't store bills in an archive. This is a concierge system, not an
accounting- or tax system.

Since this is sample code for illustrating a programming method, we
don't persist the repositories.
What's more, the repositories are mere "cardboard props". In a
real application, you'd use a persistence framework or a database.

...

  • Accommodation is a specialization of reservation, i.e.
    something like a reservation for the current week. For this reason,
    the accommodation slice builds on the reservation slice, but it
    does not extend it. The accommodation mixin extends the Hotel.Base
    class, but to this end, the Hotel.Base class must be extended
    already by the reservation mixin. This dependency is different from
    extension and explained in further detail in FIXME.
  • Queueing of reservations intercepts the reservation mechanism:
    • if the Reserve method throws a NoMoreRoomsException,
      the queueing's Reserve interceptor puts the reservation requests into the queue
    • if the Cancel method finds that there is a matching reservation
      in the queue, then the matching reservation replaces the canceled
      reservation
  • Discount builds on accommodation. It intercepts the
    accommodation's CheckOut method for putting the checked out
    (accommodated) guest's name into the accommodation history, or,
    incrementing his count of visits

...

As it is typical for both mixin-based applications,
the slices share method names between themselves. After all, that's
the purpose of both approaches to separating concerns. For example,
you don't want a single Reserve method which is a mess of reservation-
and queueing logic. You want the Reserve in Hotel.Reservation to
do reservation work, and the Reserve in Hotel.Queue to do
queueing work. However, each slice implementation (mixin) sports some
extra methods, some of those are just there for testing.

...

The project Hotel.Test contains the unit-tests for the Mixin Hotel.
The file IntegrationTest.cs demonstrates how the software is
intended to be used. (No user interface exists for this sample, just tests.) In
this integration test we follow the story of Gonzo, a regular visitor
at the Mixin Hotel:

  • The test sets Calendar to the beginning of time (week 0).
    Code Block
      Calendar.Init (0);
      
  • The test puts two Gonzo-visits into the accommodation history (you
    need three visits to be eligible for a discount). (DiscountMixinMixin.DiscountVisits
    is 2.)
    Code Block
      for (int iter = 0; iter < DiscountMixinMixin.DiscountVisits - 1; iter++)
      {
        _hotel.Accommodate ("gonzo"); // accommodation
        _hotel.CheckOut ("gonzo"); // ditto
        Calendar.NextWeek();
      }
      
  • Next, the test reserves two rooms for week 1 for two different
    parties. Since the toy hotel has only two rooms, the hotel is
    filled to capacity in week 1 (note that, at this point in the test,
    we are still in week 0, so the reservations are in the future).
    Code Block
      _hotel.Reserve ("mr and mrs jones", Calendar.Week() + 1);
      _hotel.Reserve ("mr and mrs smith", Calendar.Week() + 1);
      
  • This makes it difficult for Gonzo to reserve a room for week 1
    (consequently, Gonzo's reservation attempt is put into the queue).
    Code Block
      var gonzoRoom = _hotel.Reserve ("gonzo", Calendar.Week() + 1);
      
  • Next, one of the reserving parties cancels the reservation. This
    transforms Gonzo's reservation from a queued reservation to an
    actual reservation (note: this transformation must be "confirmed",
    supposedly by an inquiring phone-call by Babara or Manuela; for testing,
    we inject an "affirmative yes/no-handler" – this will simulate Gonzo saying
    "Yes" on the phone when asked if he wants the room).
    Code Block
      _queueMixin.SetYesNoHandler (new AffirmativeYesNoHandler());
      
  • Now Mr and Mrs Jones cancel their reservation.
    Code Block
      _hotel.Cancel ("mr and mrs jones", Calendar.Week() + 1);
      
  • The test makes time pass to week 1, i.e. the week for which rooms
    have been reserved.
    Code Block
      Calendar.NextWeek();
      
  • Gonzo shows up at the front desk, the two concierges transform his
    reservation into an accommodation, i.e. Gonzo moves into his room.
    Code Block
      var gonzoReservation = _hotel.Find ("gonzo", Calendar.Week() + 1);
      
  • Gonzo enjoys his stay, consumes beer and soda, which is added to the Bill
    Code Block
      gonzoAccommodation.Bill.AddConsumption (86, 2);
      
  • By the end of the week, Gonzo shows up at the front desk again
    for check-out and paying. Since this is Gonzo's third visit, he
    gets a discount (10 percent).
    Code Block
      var gonzoTotalWithDiscount = _hotel.CheckOut ("gonzo");