Friday, June 12, 2020

Domain Driven Design Chapter 7 Summary

Chapter 7: Using the Language: An Extended Example
    • "In the earlier examples, the patterns were mostly applied one at a time, but on a real project you have to combine them. This chapter presents one elaborate example (still drastically simpler than a real project, of course)."
  • Introducing the Cargo Shipping System
    • Initial Requirements for cargo shipping company software:
      • Track key handling of customer cargo
      • Book cargo in advance
      • Send invoices to customers automatically when the cargo reaches some point in its handling
    • A diagram for an initial model is given, defining relationships between Customer, Role, Cargo, Delivery History, Delivery Specification, Handling Event, Carrier Movement, and Location
      • "A Handling Event is a discrete action taken with the Cargo, such as loading it onto a ship or clearing it through customs."
      • "Delivery Specification defines a delivery goal, which at minimum would include a destination and an arrival date, but it can be more complex."
      • "A role distinguishes the different parts played by Customers in a shipment. One is the “shipper,” one the “receiver,” one the “payer,” and so on. Because only one Customer can play a given role for a particular Cargo, the association becomes a qualified many-to-one instead of many-to-many."
      • "Carrier Movement represents one particular trip by a particular Carrier (such as a truck or a ship) from one Location to another."
      • "Delivery History reflects what has actually happened to a Cargo, as opposed to the Delivery Specification, which describes goals. A Delivery History object can compute the current Location of the Cargo by analyzing the last load or unload and the destination of the corresponding Carrier Movement."
  • Isolating the Domain: Introducing the Applications
    • Three user-level application functions:
      •  A Tracking Query that can access past and present handling of a particular Cargo
      • A Booking Application that allows a new Cargo to be registered and prepares the system for it
      • An Incident Logging Application that can record each handling of the Cargo (providing the information that is found by the Tracking Query)
  • Distinguishing Entities and Value Objects
    • In this section each of the objects are examined and identified as either entities or value objects.
    • Customer, Cargo, Handling Event, Carrier Movement, Location, and Delivery History are identified as entities.
      • There is a note on Delivery history that it has a one to one relationship  with its Cargo, and doesn't have an identity on its own.
    • Delivery Specification is identified as a value object
    • Role and Other Attributes
      • "Role says something about the association it qualifies, but it has no history or continuity. It is a value object, and it could be shared among different Cargo/Customer associations."
      • "Other attributes such as time stamps or names are value objects."
  • Designing Associations in the Shipping Domain
    • The association between Customer and Cargo should go from Cargo to Customer:
      • "It would be cumbersome for the Customer entity to cart around every Cargo it was ever involved in. A repository could provide access in the other direction."
    • "[The association between Cargo and Delivery History] remains bidirectional. Tracking is core to Cargo in this application. A history must refer to its subject."
    • There should be a one directional association from Cargo to Delivery Specification:
      • "Value objects usually shouldn’t reference their owners. The concept of Delivery Specification actually relates more to Delivery History than to Cargo."
    • The association goes from Handling Event to Carrier Movement:
      • "The directionality of this association relieves developers from dealing with the multiplicity in the untraversed direction."
    • "An ENTITY as basic as Location may be used by many objects for many reasons. This is only practical if it is unburdened of tracking its users."
  • Aggregate Boundaries
    • Cargo, Role, Delivery History, and Delivery Specification are grouped into a single aggregate.
      • Cargo
        • "Root of aggregate. Globally unique identifier."
      • Delivery History
        • "Internal to aggregate. Delivery History has meaning and identity only in association with Cargo."
    • Handling Event was kept separate
      • "A Handling Event needs to be created in a low-contention transaction—one reason to make it the root of its own aggregate."
  • Selecting Repositories
    • Needs are identified for a Customer Repository, a Cargo Repository, a Location Repository, and a Carrier Movement Repository.
    • "For now there is no Handling Event Repository, because we decided to implement the association with Delivery History as a collection in the first iteration, and we have no application requirement to find out what has been loaded onto a Carrier Movement. Either of these reasons could change; if they did, then we would add a repository."
  • Walking Through Scenarios
    • "To cross-check all these decisions, we have to constantly step through scenarios to confirm that we can solve application problems effectively."
    • Sample Application Feature: Changing the Destination of a Cargo
      • "Occasionally a Customer calls up and says, “Oh no! We said to send our cargo to Hackensack, but we really need it in Hoboken.” We are here to serve, so the system is required to provide for this change."
      • "Delivery Specification is a value object, so it would be simplest to just to throw it away and get a new one, then use a setter method on Cargo to replace the old one with the new one."
    • Sample Application Feature: Repeat Business
      • "The users say that repeated bookings from the same Customers tend to be similar, so they want to use old Cargoes as prototypes for new ones."
      • The example goes through in detail the necessary steps of using a previous cargo as a prototype, evaluating what to copy and what to modify.
      • "Notice that we have copied everything inside the Cargo aggregate boundary, we have made some modifications to the copy, but we have affected nothing outside the aggregate boundary at all."
  • Object Creation
    • Factories and Constructors for Cargo
      • For the "Repeat Business" we could consider a factory method on Cargo like this:

        public Cargo copyPrototype(String newTrackingID)

        Or perhaps a standalone factory:

        public Cargo newCargo(Cargo prototype, String newTrackingID)

        The standalone factory could perhaps encapsulate the process of getting the auto generated id, and could be simplified to this:

        public Cargo newCargo(Cargo prototype)
    • Adding a Handling Event
      • "Every class must have primitive constructors. Because the Handling Event is an entity, all attributes that define its identity must be passed to the constructor. As discussed previously, the Handling Event is uniquely identified by the combination of the ID of its Cargo, the completion time, and the event type."
      • Constructor could look like this:

        public HandlingEvent(Cargo c, String eventType, Date timeStamp) {
            handled = c;
            type = eventType;
            completionTime = timeStamp;
        }
      • "In this case, all attributes of the Handling Event are going to be set in the initial transaction and never altered (except possibly for correcting a data-entry error), so it could be convenient, and make client code more expressive, to add a simple factory method to Handling Event for each event type, taking all the necessary arguments. For example, a “loading event” does involve a Carrier Movement:"

        public static HandlingEvent newLoading(
            Cargo c, CarrierMovement loadedOnto, Date timeStamp) {
           
            HandlingEvent result = new HandlingEvent(c, LOADING_EVENT, timeStamp);
            result.setCarrierMovement(loadedOnto);
            return result;
        }
  • Pause for Refactoring: An Alternative Design of the Cargo Aggregate
    • There are complications that arise from the circular reference from Cargo to Delivery History to Handling Event, and back to Cargo again. Specifically, any time that a Handling Event is added, it must traverse the Cargo to the Delivery History and update the Delivery History to add the Handling Event.
    • This can be fixed by adding a Handling Event Repository, removing the persistent Delivery History reference from the Cargo, and instead recreate the Delivery History through calls to the repository on an as needed basis.
  • Modules in the Shipping Model
    • To show the importance of modules, the cargo delivery software is further built out with more moving pieces. The author the proceeds to show example module groupings in two diagrams to illustrate the point that poor grouping leads to high coupling. 
    • In one diagram, he groups modules as Entities, Values, and Services, and the diagram has a multiplicity of connections moving between each of the packages.
    • In a subsequent diagram, the same objects are instead grouped in the modules Customer, Billing, and Shipping, with a max of two connections between each module.
  • Introducing a New Feature: Allocation Checking
    • At this point, a new feature is requested that wasn't part of the original specification. This feature deals with allocating how much of any given product can be sold so that more profitable business transactions will not be crowded out by less profitable transactions. This will involve integrating the software with the software used in the sales department.
    • Connecting the Two Systems
      • "The Sales Management System was not written with the same model in mind that we are working with here. If the Booking Application interacts with it directly, our application will have to accommodate the other system’s design, which will make it harder to keep a clear model-driven design and will confuse the ubiquitous language. Instead, let’s create another class whose job it will be to translate between our model and the language of the Sales Management System. It will not be a general translation mechanism. It will expose just the features our application needs, and it will reabstract them in terms of our domain model. This class will act as an anticorruption layer (discussed in Chapter 14)."
    • Enhancing the Model: Segmenting the Business
      • Our system does not yet define "type" for Cargo. We could point blank use the information coming from the sales system, but there's a potentially better way.
      • An analysis pattern that could be used is the enterprise segment.
      • "An enterprise segment is a set of dimensions that define a way of breaking down a business. These dimensions could include all those mentioned already for the shipping business, as well as time dimensions, such as month to date."
      • This will be further discussed in chapter 11.
    • Performance Tuning
      • Depending on various situations or limitations with the other system that we must integrate with, various trade offs may need to be made in order to ensure performance.
  • A Final Look
    • "That’s it. This integration could have turned our simple, conceptually consistent design into a tangled mess, but now, using an anticorruption layer, a service, and some enterprise segments, we have integrated the functionality of the Sales Management System into our booking system cleanly, enriching the domain."
    • "A final design question: Why not give Cargo the responsibility of deriving the Enterprise Segment? At first glance it seems elegant, if all the data the derivation is based on is in the Cargo, to make it a derived attribute of Cargo. Unfortunately, it is not that simple. Enterprise Segments are defined arbitrarily to divide along lines useful for business strategy. The same entities could be segmented differently for different purposes. We are deriving the segment for a particular Cargo for booking allocation purposes, but it could have a completely different Enterprise Segment for tax accounting purposes. Even the allocation Enterprise Segment could change if the Sales Management System is reconfigured because of a new sales strategy. So the Cargo would have to know about the Allocation Checker, which is well outside its conceptual responsibility, and it would be laden with methods for deriving specific types of Enterprise Segment. Therefore, the responsibility for deriving this value lies properly with the object that knows the rules for segmentation, rather than the object that has the data to which those rules apply. Those rules could be split out into a separate “Strategy” object, which could be passed to a Cargo to allow it to derive an Enterprise Segment. That solution seems to go beyond the requirements we have here, but it would be an option for a later design and shouldn’t be a very disruptive change."