Friday, July 31, 2020

Domain Driven Design Chapter 11 Summary

Chapter 11: Applying Analysis Patterns
    • "Deep models and supple designs don’t come easily. Progress comes from lots of learning about the domain, lots of talking, and lots of trial and error. Sometimes, though, we can get a leg up."
    • "When an experienced developer looking at a domain problem sees a familiar sort of responsibility or a familiar web of relationships, he or she can draw on the memory of how the problem was solved before. [...] Some of these patterns have been documented and shared, allowing the rest of us to draw on the accumulated experience."
    • "They let us cut through expensive trial and error to start with a model that is already expressive and implementable and addresses subtleties that might be costly to learn. From that starting point, we refactor and experiment. These are not out-of-the-box solutions."
    • "Analysis patterns are groups of concepts that represent a common construction in business modeling. It may be relevant to only one domain or it may span many domains.' ~Martin Fowler 
  • Analysis Patterns are Knowledge to Draw On
    • "When you are lucky enough to have an analysis pattern, it hardly ever is the answer to your particular needs. Yet it offers valuable leads in your investigation, and it provides cleanly abstracted vocabulary. It should also give you guidance about implementation consequences that will save you pain down the road."
    • "Sometimes the result doesn’t even obviously relate to the analysis pattern itself, yet was stimulated by the insights from the pattern."
    • "There is one kind of change you should avoid. When you use a term from a well-known analysis pattern, take care to keep the basic concept it designates intact, however much the superficial form might change. There are two reasons for this. First, the pattern may embed understanding that will help you avoid problems. Second, and more important, your ubiquitous language is enhanced when it includes terms that are widely understood or at least well explained. If your model definitions change through the natural evolution of the model, take the trouble to change the names too."
    • "This kind of reapplication of organized knowledge is completely different from attempts to reuse code through frameworks or components, except that either could provide the seed of an idea that is not obvious. A model, even a generalized framework, is a complete working whole, while an analysis is a kit of model fragments. Analysis patterns focus on the most critical and difficult decisions and illuminate alternatives and choices. They anticipate downstream consequences that are expensive if you have to discover them for yourself."

Wednesday, July 15, 2020

Domain Driven Design Chapter 10 Summary

Chapter 10: Supple Design
    • "When software with complex behavior lacks a good design, it becomes hard to refactor or combine elements. Duplication starts to appear as soon as a developer isn’t confident of predicting the full implications of a computation. Duplication is forced when design elements are monolithic, so that the parts cannot be recombined. Classes and methods can be broken down for better reuse, but it gets hard to keep track of what all the little parts do. When software doesn’t have a clean design, developers dread even looking at the existing mess, much less making a change that could aggravate the tangle or break something through an unforeseen dependency. In any but the smallest systems, this fragility places a ceiling on the richness of behavior it is feasible to build. It stops refactoring and iterative refinement.

      To have a project accelerate as development proceeds—rather than get weighed down by its own legacy—demands a design that is a pleasure to work with, inviting to change. A supple design."
    • "There is no formula for designing software like this, but I have culled a set of patterns that, in my experience, tend to lend suppleness to a design when they fit. These patterns and examples should give a feel for what a supple design is like and the kind of thinking that goes into it."
  • Intention-Revealing Interfaces
    • "If a developer must consider the implementation of a component in order to use it, the value of encapsulation is lost. If someone other than the original developer must infer the purpose of an object or operation based on its implementation, that new developer may infer a purpose that the operation or class fulfills only by chance. If that was not the intent, the code may work for the moment, but the conceptual basis of the design will have been corrupted, and the two developers will be working at cross-purposes."
    • "Name classes and operations to describe their effect and purpose, without reference to the means by which they do what they promise. This relieves the client developer of the need to understand the internals. These names should conform to the ubiquitous language so that team members can quickly infer their meaning. Write a test for a behavior before creating it, to force your thinking into client developer mode."
  • Side-Effect-Free Functions
    • "Interactions of multiple rules or compositions of calculations become extremely difficult to predict. The developer calling an operation must understand its implementation and the implementation of all its delegations in order to anticipate the result. The usefulness of any abstraction of interfaces is limited if the developers are forced to pierce the veil. Without safely predictable abstractions, the developers must limit the combinatory explosion, placing a low ceiling on the richness of behavior that is feasible to build."
    • "Place as much of the logic of the program as possible into functions, operations that return results with no observable side effects. Strictly segregate commands (methods that result in modifications to observable state) into very simple operations that do not return domain information. Further control side effects by moving complex logic into value objects when a concept fitting the responsibility presents itself."
    • "Side-effect-free functions, especially in immutable value objects, allow safe combination of operations. When a function is presented through an intention-revealing interface, a developer can use it without understanding the detail of its implementation."
  • Assertions
    • "When the side effects of operations are only defined implicitly by their implementation, designs with a lot of delegation become a tangle of cause and effect. The only way to understand a program is to trace execution through branching paths. The value of encapsulation is lost. The necessity of tracing concrete execution defeats abstraction."
    • "State post-conditions of operations and invariants of classes and aggregates. If assertions cannot be coded directly in your programming language, write automated unit tests for them. Write them into documentation or diagrams where it fits the style of the project’s development process.

      Seek models with coherent sets of concepts, which lead a developer to infer the intended assertions, accelerating the learning curve and reducing the risk of contradictory code."
  • Conceptual Contours
    • "When elements of a model or design are embedded in a monolithic construct, their functionality gets duplicated. The external interface doesn’t say everything a client might care about. Their meaning is hard to understand, because different concepts are mixed together.

      On the other hand, breaking down classes and methods can pointlessly complicate the client, forcing client objects to understand how tiny pieces fit together. Worse, a concept can be lost completely. Half of a uranium atom is not uranium. And of course, it isn’t just grain size that counts, but just where the grain runs."
    • "The goal is a simple set of interfaces that combine logically to make sensible statements in the ubiquitous language, and without the distraction and maintenance burden of irrelevant options. This is typically an outcome of refactoring: it’s hard to produce up front. But it may never emerge from technically oriented refactoring; it emerges from refactoring toward deeper insight."
  • Standalone Classes
    • "Even within a module, the difficulty of interpreting a design increases wildly as dependencies are added. This adds to mental overload, limiting the design complexity a developer can handle. Implicit concepts contribute to this load even more than explicit references."
    • "Low coupling is fundamental to object design. When you can, go all the way. Eliminate all other concepts from the picture. Then the class will be completely self-contained and can be studied and understood alone. Every such self-contained class significantly eases the burden of understanding a module."
    • "Dependencies on other classes within the same module are less harmful than those outside. Likewise, when two objects are naturally tightly coupled, multiple operations involving the same pair can actually clarify the nature of the relationship. The goal is not to eliminate all dependencies, but to eliminate all nonessential ones. If every dependency can’t be eliminated, each one that is removed frees the developer to concentrate on the remaining conceptual dependencies."
    • "Try to factor the most intricate computations into standalone classes, perhaps by modeling value objects held by the more connected classes"
    • "Eliminating dependencies should not mean dumbing down the model by arbitrarily reducing everything to primitives."
  • Closure of Operations
    • "Another common practice in refined designs is what I call “closure of operations.” The name comes from that most refined of conceptual systems, mathematics. 1 + 1 = 2. The addition operation is closed under the set of real numbers. Mathematicians are fanatical about not introducing extraneous concepts, and the property of closure provides them a way of defining an operation without involving any other concepts. We are so accustomed to the refinement of mathematics that it can be hard to grasp how powerful its little tricks are. But this one is used extensively in software designs as well. The basic use of XSLT is to transform one XML document into another XML document. This sort of XSLT operation is closed under the set of XML documents. The property of closure tremendously simplifies the interpretation of an operation, and it is easy to think about chaining together or combining closed operations."
    • "Where it fits, define an operation whose return type is the same as the type of its argument(s). If the implementer has state that is used in the computation, then the implementer is effectively an argument of the operation, so the argument(s) and return value should be of the same type as the implementer. Such an operation is closed under the set of instances of that type. A closed operation provides a high-level interface without introducing any dependency on other concepts."
  • Declarative Design
    • "This term means many things to many people, but usually it indicates a way to write a program, or some part of a program, as a kind of executable specification. A very precise description of properties actually controls the software. In its various forms, this could be done through a reflection mechanism or at compile time through code generation (producing conventional code automatically, based on the declaration). This approach allows another developer to take the declaration at face value. It is an absolute guarantee. "
    • Pitfalls to watch out for:
      • "A declaration language not expressive enough to do everything needed, but a framework that makes it very difficult to extend the software beyond the automated portion"
      • "Code-generation techniques that cripple the iterative cycle by merging generated code into handwritten code in a way that makes regeneration very destructive"
    • "The unintended consequence of many attempts at declarative design is the dumbing-down of the model and application, as developers, trapped by the limitations of the framework, enact design triage in order to get something delivered."
    • "The greatest value I’ve seen delivered has been when a narrowly scoped framework automates a particularly tedious and error-prone aspect of the design, such as persistence and object-relational mapping. The best of these unburden developers of drudge work while leaving them complete freedom to design."
    • Domain-Specific Languages
      • "An interesting approach that is sometimes declarative is the domainspecific language. In this style, client code is written in a programming language tailored to a particular model of a particular domain. [...] In such a language, programs can be extremely expressive, and make the strongest connection with the ubiquitous language. This is an exciting concept, but domain-specific languages also have their drawbacks in the approaches I’ve seen based on object-oriented technology."
      • "To refine the model, a developer needs to be able to modify the language. This may involve modifying grammar declarations and other language-interpreting features, as well as modifying underlying class libraries. I’m all in favor of learning advanced technology and design concepts, but we have to soberly assess the skills of a particular team, as well as the likely skills of future maintenance teams."
  • A Declarative Style of Design
    • "Once your design has intention-revealing interfaces, side-effect-free functions, and assertions, you are edging into declarative territory. Many of the benefits of declarative design are obtained once you have combinable elements that communicate their meaning, and have characterized or obvious effects, or no observable effects at all."
    • Extending Specifications in a Declarative Style
      • "Specification is an adaptation of an established formalism, the predicate. Predicates have other useful properties that we can draw on, selectively."
      • Combining Specifications Using Logical Operators
        • "When using specifications, you quickly come across situations in which you would like to combine them. As just mentioned, a specification is an example of a predicate, and predicates can be combined and modified with the operations “AND,” “OR,” and “NOT.” These logical operations are closed under predicates, so specification combinations will exhibit closure of operations."
        • "Also, this full generality is not needed in many cases. In particular, AND tends to be used a lot more than the others, and it also tends to create less implementation complexity. Don’t be afraid to implement only AND, if that is all you need."
      • Subsumption
        • "This final feature is not usually needed and can be difficult to implement, but every now and then it solves a really hard problem. It also elucidates the meaning of a specification."
        • "A more stringent spec subsumes a less stringent one. It could take its place without any previous requirement being neglected."
        • "In the language of SPECIFICATION, we would say that the new SPECIFICATION subsumes the old SPECIFICATION, because any candidate that would satisfy the new SPEC would also satisfy the old.

          If each of these SPECIFICATIONS is viewed as a predicate, subsumption is equivalent to logical implication. Using conventional notation, A➝ B means that statement A implies statement B, so that if A is true, B is also true."
        • "Unfortunately, when OR and NOT are included, these proofs become much more involved. In most situations it is best to avoid such complexity by making a choice, either forgoing some of the operators or forgoing subsumption. If both are needed, consider carefully if the benefit is great enough to justify the difficulty."
  • Angles of Attack
    • "You can’t just look at an enormous system and say, “Let’s make this supple.” You have to choose targets."
    • Carve off Subdomains
      • "You just can’t tackle the whole design at once. Pick away at it. Some aspects of the system will suggest approaches to you, and they can be factored out and worked over. [...] With each such step, not only is the new module clean, but also the part left behind is smaller and clearer. [...] It is more useful to make a big impact on one area, making a part of the design really supple, than to spread your efforts thin."
    • Draw on Established Formalisms, When You Can
      • "Creating a tight conceptual framework from scratch is something you can’t do every day. Sometimes you discover and refine one of these over the course of the life of a project. But you can often use and adapt conceptual systems that are long established in your domain or others, some of which have been refined and distilled over centuries."
      • "There are many such formalized conceptual frameworks, but my personal favorite is math. It is surprising how useful it can be to pull out some twist on basic arithmetic. Many domains include math somewhere. Look for it. Dig it out. Specialized math is clean, combinable by clear rules, and people find it easy to understand."

Friday, July 10, 2020

Domain Driven Design Chapter 9 Summary

Chapter 9: Making Implicit Concepts Explicit
    • "A deep model has power because it contains the central concepts and abstractions that can succinctly and flexibly express essential knowledge of the users’ activities, their problems, and their solutions. The first step is to somehow represent the essential concepts of the domain in the model. Refinement comes later, after successive iterations of knowledge crunching and refactoring. But this process really gets into gear when an important concept is recognized and made explicit in the model and design."
    • "Many transformations of domain models and the corresponding code happen when developers recognize a concept that has been hinted at in discussion or present implicitly in the design, and they then represent it explicitly in the model with one or more objects or relationships."
  • Digging Out Concepts
    • Listen to Language
      • "Listen to the language the domain experts use. Are there terms that succinctly state something complicated? Are they correcting your word choice (perhaps diplomatically)? Do the puzzled looks on their faces go away when you use a particular phrase? These are hints of a concept that might benefit the model."
      • "This is not the old “nouns are objects” notion. Hearing a new word produces a lead, which you follow up with conversation and knowledge crunching, with the goal of carving out a clean, useful concept."
    • Scrutinize Awkwardness
      • "The concept you need is not always floating on the surface, emerging in conversation or documents. You may have to dig and invent. The place to dig is the most awkward part of your design. The place where procedures are doing complicated things that are hard to explain. The place where every new requirement seems to add complexity."
      • "Now you have to actively engage the domain experts in the search. If you are lucky, they may enjoy playing with ideas and experimenting with the model. If you are not that lucky, you and your fellow developers will have to come up with the ideas, using the domain expert as a validator, watching for discomfort or recognition on his or her face."
    • Contemplate Contradictions
      • "Different domain experts see things different ways based on their experience and needs. Even the same person provides information that is logically inconsistent after careful analysis. Such pesky contradictions, which we encounter all the time when digging into program requirements, can be great clues to deeper models."
    • Read the Book
      • "Different domain experts see things different ways based on their experience and needs. Even the same person provides information that is logically inconsistent after careful analysis. Such pesky contradictions, which we encounter all the time when digging into program requirements, can be great clues to deeper models."
    • Try, Try Again
      • "The examples I’ve given don’t convey the amount of trial and error involved. I might follow half a dozen leads in conversation before finding one that seems clear and useful enough to try out in the model. I’ll end up replacing that one at least once later, as additional experience and knowledge crunching serve up better ideas."
      • "All these changes of direction are not just thrashing. Each change embeds deeper insight into the model."
  • How to Model Less Obvious Kinds of Concepts
    • "Things, event very abstract ones [...], are the meat of most object models, along with the actions those things take. [...] But other important categories of concepts can be made explicit in a model as well."
    • Explicit Constraints
      • "Constraints make up a particularly important category of model concepts. They often emerge implicitly, and expressing them explicitly can greatly improve a design."
      • "[W]arning signs that a constraint is distorting the design of its host object.
        • "Evaluating a constraint requires data that does not otherwise fit the object’s definition."
        • "Related rules appear in multiple objects, forcing duplication or inheritance between objects that are not otherwise a family"
        • "A lot of design and requirements conversation revolves around the constraints, but in the implementation, they are hidden away in procedural code."
    • Processes as Domain Objects
      • "What I am talking about here are processes that exist in the domain, which we have to represent in the model. When these emerge, they tend to make for awkward object designs."
      • "The key to distinguishing a process that ought to be made explicit from one that should be hidden is simple: Is this something the domain experts talk about, or is it just part of the mechanism of the computer program?"
    • Specification
      • "Business rules often do not fit the responsibility of any of the obvious entities or value objects, and their variety and combinations can overwhelm the basic meaning of the domain object. But moving the rules out of the domain layer is even worse, since the domain code no longer expresses the model."
      • "Create explicit predicate-like value objects for specialized purposes. A specification is a predicate that determines if an object does or does not satisfy some criteria"
    • Applying and Implementing Specification
      • "Much of the value of SPECIFICATION is that it unifies application functionality that may seem quite different. We might need to specify the state of an object for one or more of these three purposes. "
        • Validation - "To validate an object to see if it fulfills some need or is ready for some purpose"
          • Given an object, return whether or not the specification is satisfied by the object.
        • Selection (Querying) - "To select an object from a collection (as in the case of querying for overdue invoices)"
          • While this could be applied at the code level using the same code used for specification, there are a number a situations where this would prove impractical, and instead approaches of specifying the specification so that it can be applied at a sql query level may be necessary, and various approaches for this are discussed.
        • Building to Order (Generating) - "To specify the creation of a new object to fit some need"
          • You can create a generator that takes in a specification to satisfy, and the generator will return an object that satisfies the specification.
          • "Building to order can mean creation of an object from scratch, but it can also be a configuration of preexisting objects to satisfy the spec."

Friday, June 19, 2020

Domain Driven Design Chapter 8 Summary

Chapter 8: Breakthrough
    • "The returns from refactoring are not linear. Usually there is a marginal return for a small effort, and the small improvements add up. They fight entropy, and they are the frontline protection against a fossilized legacy. But some of the most important insights come abruptly and send a shock through the project."
    • "This sort of breakthrough is not a technique; it is an event. The challenge lies in recognizing what is happening and deciding how to deal with it. To convey what this experience feels like, I’ll tell a true story of a project I worked on some years ago, and how we arrived at a very valuable deep model."
  • Story of a Breakthrough
      • "When Intel wants to build a billion-dollar factory, they need a loan that is too big for any single lending company to take on, so the lenders form a syndicate that pools its resources to support a facility [...]. An investment bank usually acts as syndicate leader, coordinating transactions and other services. Our project was to build software to track and support this whole process."
      • "In the domain of commercial banking, a facility is a commitment by a company to lend."
    • A Decent Model, and Yet...
      • "We were feeling pretty good. Four months before, we had been in deep trouble with a completely unworkable, inherited code base, which we had since wrestled into a coherent model-driven design. The model [...] makes the common case very simple. [...] But there were some disconcerting signs. We kept stumbling over unexpected requirements that complicated the design."
    • The Breakthrough
      • "Suddenly one week it dawned on us what was wrong. Our model tied together the Facility and Loan shares in a way that was not appropriate to the business. This revelation had wide repercussions. With the business experts nodding, enthusiastically helping—and, I dare say, wondering what took us so long—we hashed out a new model on a whiteboard. Although the details hadn’t jelled yet, we knew the crucial feature of the new model: shares of the Loan and those of the Facility could change independently of each other."
    • A Deeper Model
      • "[We had] the realization that our “Investments” and “Loan Investments” were just two special cases of a general and fundamental concept: shares. Shares of a facility, shares of a loan, shares of a payment distribution. Shares, shares everywhere. Shares of any divisible value. "
      • "There were no longer specialized objects for the shares of a Facility or a Loan. They both were broken down into the more intuitive “Share Pie.” This generalization allowed the introduction of “shares math,” vastly simplifying the calculation of shares in any transaction, and making those calculations more expressive, concise, and easily combined."
      • "But most of all, problems went away because the new model removed an inappropriate constraint. It freed the Loan’s Shares to depart from the proportions of the Facility’s Shares, while keeping in place the valid constraints on totals, fee distributions, and so on. The Share Pie of the Loan could be adjusted directly, so the Loan Adjustment was no longer needed, and a large amount of special-case logic was eliminated."
      • "Suddenly, on the basis of this new way of looking at the domain, we could run through every scenario we had ever encountered relatively effortlessly, much more simply than ever before. And our model diagrams made perfect sense to the business experts, who had often indicated that the diagrams were “too technical” for them. Even just sketching on a whiteboard, we could see that our most persistent rounding problems would be pulled out by the roots, allowing us to scrap some of the complicated rounding code."
    • A Sobering Decision
      • "You might reasonably assume that we would have been elated at this point. We were not. We were under a severe deadline; the project was already dangerously behind schedule. Our dominant emotion was fear."
      • "The gospel of refactoring is that you always go in small steps, always keeping everything working. But to refactor our code to this new model would require changing a lot of supporting code, and there would be few, if any, stable stopping points in between."
      • Conversation with project manager:
        • "Q: How long would it take to get back to current functionality with the new design?"

          "A: About three weeks."

          "Q: Could we solve the problems without it?"

          "A: Probably. But no way to be sure."

          "Q: Would we be able to move forward in the next release if we didn’t do it now?"

          "A: Forward movement would be slow without the change. And the change would be much harder once we had an installed base."

          "Q: Did we think it was the right thing to do?"

          "A: We knew the political situation was unstable, so we’d cope if we had to. And we were tired. But, yes, it was a simpler solution that fit the business much better. In the long run it was lower risk."
    • The Payoff
      • "The mystifyingly unexpected requirement changes stopped. The rounding logic, though never exactly simple, stabilized and made sense."
      • "As version two evolved, this Share Pie became the unifying theme of the whole application. Technical people and business experts used it to discuss the system. Marketing people used it to explain the features to prospective customers. Those prospects and customers immediately grasped it and used it to discuss features. It truly became part of the ubiquitous language because it got to the heart of what loan syndication is about."
  • Opportunities
    • "Much as we might like it to be otherwise, progress isn’t a smooth ride. The transition to a really deep model is a profound shift in your thinking and demands a major change to the design. On many projects the most important progress in model and design come in these breakthroughs."
  • Focus on Basics
    • "Don’t become paralyzed trying to bring about a breakthrough. The possibility usually comes after many modest refactorings. Most of the time is spent making piecemeal improvements, with model insights emerging gradually during each successive refinement."
  • Epilogue: A Cascade of New Insights
    • "That breakthrough got us out of the woods, but it was not the end of the story. The deeper model opened unexpected opportunities to make the application richer and the design clearer."
    • "As is often the case after a real breakthrough to a deep model, the clarity and simplicity of the new design, combined with the enhanced communication based on the new ubiquitous language, had led to yet another modeling breakthrough."

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."

Thursday, June 4, 2020

Domain Driven Design Chapter 6 Summary

Chapter 6: The Life Cycle of a Domain Object
    • The challenges fall into two categories:
      • Maintaining integrity throughout the life cycle.
      • Preventing the model from getting swamped by the complexity of managing the life cycle.
    • Three patterns to address these issues:
      • Aggregates
        • "[T]ighten up the model itself by defining clear ownership and boundaries, avoiding a chaotic, tangled web of objects."
      • Factories
        • "[C]reate and reconstitute complex objects and aggregates, keeping their internal structure encapsulated."
      • Repositories
        • "[A]ddress the middle and end of the life cycle, providing the means of finding and retrieving persistent objects while encapsulating the immense infrastructure involved."
  • Aggregates
    • "It is difficult to guarantee the consistency of changes to objects in a model with complex associations. Invariants need to be maintained that apply to closely related groups of objects, not just discrete objects. Yet cautious lacking schemes cause multiple users to interfere pointlessly with each other and make a system unusable."
    • "[H]ow do we know where an object made up of other objects begins and ends?"
    • "Although this problem surfaces as technical difficulties in database transactions, it is rooted in the model -- in its lack of defined boundaries. A solution driven from the model will make the model easier to understand and make the design easier to communicate."
    • "An aggregate is a cluster of associated objects that we treat as a unit for the purposes of data changes. Each aggregate has a root and a boundary. The boundary defines what is inside the aggregate. The root is a single, specific entity contained in the aggregate. The root is the only member of the aggregate that outside objects are allowed to hold references to, although objects within the boundary may hold references to each other. Entities other than the root have local identity, but that identity needs to be distinguishable only within the aggregate, because no outside object can ever see it out of the context of the root entity."
    • "Invariants, which are consistency rules that must be maintained whenever data changes, will involve relationships between members of the aggregate. Any rule that spans aggregates will not be expected to be up-to-date at all times. Through event processing, batch processing, or other update mechanisms, other dependencies can be resolved within some specified time. But the invariants applied within an aggregate will be enforced with the completion of each transaction."
    • Rules to apply to all transactions
      • The root has global identity and is responsible for checking invariants.
      • Entities inside the boundary have local identity, unique only within the aggregate.
      • Nothing outside the boundary can hold a reference to anything inside, except the root entity. The root entity can hand out references to internal entities, but they can only be used transiently, and a reference cannot be held. The root may hand out a copy of a value object, and it doesn't matter what happens to it, because it's just a value and no longer has association with the aggregate.
      • Only aggregate roots can be obtained directly with database queries. All other objects must be found by traversal of associations.
      • Objects within the aggregate can hold references to other aggregate roots.
      • A delete operation must remove everything within the aggregate at once.
      • When a change to any object within the aggregate boundary is committed, all invariants of the whole aggregate must be satisfied.
  • Factories
    • "When creation of an object, or an entire aggregate, becomes complicated or reveals too much of the internal structure, factories provide encapsulation."
    • "An object should be distilled until nothing remains that does not relate to its meaning or support its role in interactions. This mid-life cycle responsibility is plenty. Problems arise from overloading a complex object with responsibility for its own creation."
    • "Creation of an object can be a major operation in itself, but complex assembly operations do not fit the responsibility of the created objects. Combining such responsibilities can produce ungainly designs that are hard to understand. Making the client direct construction muddies the design of the client, breaches encapsulation of the assembled object or aggregate, and overly couples the client to the implementation of the created object."
    • The two basic requirements for any good factory are:
      • Each creation method is atomic and enforces all invariants of the created object or aggregate.
      • The factory should be abstracted to the type desired, rather than the concrete class(es) created.
    • Choosing Factories and Their Sites
      • "Generally speaking, you create a factory to build something whose details you want to hide, and you place the factory where you want the control to be. These decisions usually revolve around aggregates."
      • "A factory is very tightly coupled to its product, so a factory should be attached only to an object that has a close natural relationship with the product. When there is something we want to hide [...] yet there doesn't seem to be a natural host, we must create a dedicated factory object or service."
    • When a Constructor is All You Need
      • "[T]here are times when the directness of a constructor makes it the best choice. Factories can actually obscure simple objects that don't use polymorphism."
      • The trade-offs favor a bare, public constructor in the following circumstances.
        • The class is the type. It is not part of any interesting hierarchy, and it isn't used polymorphically by implementing an interface.
        • The client cares about the implementation, perhaps as a way of choosing a strategy
        • All of the attributes of the object are available to the client, so that no object creation gets nested inside the constructor exposed to the client.
        • The construction is not complicated.
        • A public constructor must follow the same rules as a factory: It must be an atomic operation that satisfies all invariants of the created object.
      • "Constructors should be dead simple. Complex assemblies, especially of aggregates, call for factories. The threshold for choosing to use a little factory method isn't high."
    • Designing the Interface
      • Two points to keep in mind
        • Each operation must be atomic.
          • You have to pass in everything needed to create a complete product in a single interaction with the factory. You also have to decide what will happen if creation fails, in the event that some invariant isn't satisfied. You could throw an exception or just return null. To be consistent, consider adopting a coding standard for failures in factories.
        • The factory will be coupled to its arguments.
          • If you are not careful in your selection of input parameters, you can create a rat's nest of dependencies. The degree of coupling will depend on what you do with the argument. If it is simply plugged into the product, you've created a modest dependency. If you are picking parts out of the argument to use in the construction, the coupling gets tighter.
    • Where Does Invariant Logic Go?
      • "[Y]ou should think twice before removing the rules applying to an object outside that object. The factory can delegate invariant checking to the product, and this is often best."
      • "Under some circumstances, there are advantages to placing invariant logic in the factory and reducing clutter in the product. This is especially appealing with aggregate rules (which span many objects). It is especially unappealing with factory methods attached to other domain objects."
      • "An object doesn't need to carry around logic that will never be applied in its active lifetime. In such cases, the factory is a logical place to put invariants, keeping the product simpler."
    • Entity Factories Versus Value Object Factories
      • Entity factories differ from value object factories in two ways.
        • "Value objects are immutable; the product comes out complete in its final form. So the factory operations have to allow for a full description of the product. Entity factories tend to take just the essential attributes required to make a valid aggregate. Details can be added later if they are not required by an invariant."
        • "Then there is the issues involved in assigning identity to an entity -- irrelevant to a value object. [...] When the program is assigning an identifier, the factory is a good place to control it. Although the actual generation of a unique tracking id is typically done by a database "sequence" or other infrastructure mechanism, the factory knows what to ask for and where to put it."
    • Reconstituting Stored Objects
      • A factory used for reconstitution is very similar to one used for creation, with two major differences.
        • An entity factory used for reconstitution does not assign a new tracking ID.
          • "To do so would lose the continuity with the object's previous incarnation. So identifying attributes must be part of the input parameters in a factory reconstituting a stored object."
        • A factory reconstituting an object will handle violation of an invariant differently.
          • "During creation of a new object, a factory should simply balk when an invariant isn't met, but a more flexible response may be necessary in reconstitution. If an object already exists somewhere in the system (such as in the database), this fact cannot be ignored. Yet we also can't ignore the rule violation. There has to be some strategy for repairing such inconsistencies, which can make reconstitution more challenging than the creation of new objects."
  • Repositories
    • "Associations allow us to find an object based on its relationship to another. But we must have a starting point for a traversal to an entity or value in the middle of its life cycle."
    • "A database search is globally accessible and makes it possible to go directly to any object. There is no need for all objects to be interconnected, which allows us to keep the web of objects manageable. Whether to provide a traversal or depend on a search becomes a design decision, trading off the decoupling of the search against the cohesiveness of the association."
    • "[Developers] may use queries to pull the exact data they need from the database, or to pull a few specific objects rather than navigating from aggregate roots. Domain logic moves into queries and client code, and the entities and value objects become mere data containers. The sheer technical complexity of applying most database access infrastructure quickly swamps the client code, which leads developers to dumb down the domain layer, which makes the model irrelevant."
    • Querying a Repository
      • "Although most queries return an object or a collection of objects, it also fits within the concept to return some types of summary calculations, such as an object count, or a sum of a numerical attribute that was intended by the model to be tallied."
    • Client Code Ignores Repository Implementation; Developers Do Not
      • "Encapsulation of the persistence technology allows the client to be very simple, completely decoupled from the implementation of the repository. But as is often the case with encapsulation, the developer must understand what is happening under the hood. The performance implications can be extreme when repositories are used in different ways or work in different ways.
    • Implementing a Repository
      • "The ideal is to hid all the inner workings from the client (although not from the developer of the client), so that the client code will be the same whether the data is stored in an object database, stored in a relational database, or simply held in memory. [...] Encapsulating the mechanisms of storage, retrieval, and query is the most basic feature of a repository implementation."
    • Working Within Your Frameworks
      • "You may find that the framework provides services you can use to easily create a repository, or you may find that the framework fights you all the way. [...] In general, don't fight your frameworks. Seek ways to keep the fundamentals of domain-driven design and let go of the specifics when the framework is antagonistic. [...] This is assuming that you have no choice but to use the framework. [...] If you have the freedom, choose frameworks, or parts of frameworks, that are harmonious with the style of design you want to use."
    • The Relationship with Factories
      • "Because the repository is [...] creating objects based on data, many people consider the repository to be a factory -- indeed it is, from a technical point of view. But it is more useful to keep the model in the forefront, and as mentioned before, the reconstitution of a stored object is not the creation of a new conceptual object. In this domain-driven view of the design, factories and repositories have distinct responsibilities. The factory makes new objects; the repository finds old objects. The client of a repository should be given the illusion that the objects are in memory."
      • "One other case that drives people to combine factory and repository is the desire for "find or create" functionality, in which a client can describe an object it wants and, if no such object is found, will be given a newly created one. This function should be avoided. It is a minor convenience at best. A lot of cases in which it seems useful go away when entities and value objects are distinguished. [...] Usually, the distinction between a new object and an existing object is important in the domain, and a framework that transparently combines them will actually muddle the situation."
  • Designing Objects for Relational Databases
    • "When the database is being viewed as an object store, don't let the data model and the object model diverge far, regardless of the powers of mapping tools. Sacrifice some richness of object relationships to keep close to the relational model. Compromise some formal relational standards, such as normalization, if it helps simplify the object mapping."
    • "Processes outside the object system should not access such an object store. They could violate the invariants enforced by the objects. Also, their access will lock in the data model so that it is hard to change when the objects are refactored."
    • "The tradition of refactoring that has increasingly taken hold in the object world has not really affected relational database design much. What's more, serious data migration issues discourage frequent change. This may create a drag on the refactoring of the object model, but if the object model and the database model start to diverge, transparency can be lost quickly."

Wednesday, May 27, 2020

Domain Driven Design Chapter 5 Summary

Chapter 5: A Model Expressed in Software
    • "Connecting model and implementation has to be done at the detail level."
  • Associations
    • "For every traversable association in the model, there is a mechanism in the software with the same properties."
    • "In real life, there are lots of many-to-many associations, and a great number are naturally bidirectional. The same tends to be true of early forms of a model as we brainstorm and explore the domain. But these general associations complicate implementation and maintenance. Furthermore, they communicate very little about the nature of the relationship."
    • Three ways of making associations more tractable
      • Imposing a traversal direction
        • "The United States has had many presidents, as have many other countries. This is a bidirectional, one-to-many relationship. Yet we seldom would start out with the name "George Washington" and ask, "Of which country was he president?" Pragmatically, we can reduce the relationship to a unidirectional association, traversable from country to president. This refinement actually reflects insight into the domain, as well as making a more practical design. It captures the understanding that one direction of the association is much more meaningful and important than the other. It keeps the "Person" class independent of the far less fundamental concept of "President."
      • Adding a qualifier, effectively reducing multiplicity
        • "Very often, deeper understanding leads to a "qualified" relationship. Looking deeper into presidents, we realize that (except in civil wars, perhaps) a country has only one president at a time. This qualifier reduces the multiplicity to one-to-one, and explicitly embeds an important rule into the model. Who was the president of the United States in 1790? George Washington."
      • Eliminating nonessential associations
        • "Of course, the ultimate simplification is to eliminate an association altogether, if it is not essential to the job at hand or the fundamental meaning of the model objects."
  • Entities (A.K.A. Reference Objects)
    • "Many objects are not fundamentally defined by their attributes, but rather by a thread of continuity and identity."
    • "many things are defined by their identity, and not by any attribute. In our typical conception, a person [...] has an identity that stretches from birth to death and even beyond. That person's physical attributes transform and ultimately disappear. The name may change. Financial relationships come and go. There is not a single attribute of a person that cannot change; yet the identity persists."
    • "Some objects are not defined primarily by their attributes. They represent a thread of identity that runs through time and often across distinct representations. Sometimes such an object must be matched with another object even though attributes differ. An object must be distinguished from other objects even though they might have the same attributes. Mistaken identity can lead to data corruption.
    • Modeling Entities
      • "[T]he most basic responsibility of entities is to establish continuity so that behavior can be clear and predictable. They do this best if they are kept spare. Rather than focusing on the attributes or even the behavior, strip  the entity object's definition down to the most intrinsic characteristics, particularly those that identify it or are commonly used to find or match it. Add only behavior that is essential to the concept and attributes that are required by that behavior. Beyond that, look to remove behavior and attributes into other objects associated with the core entity. [...] Beyond identity issues, entities tend to fulfill their responsibilities by coordinating the operations of objects they own."
    • Designing the Identity Operation
      • "Each entity must have an operational way of establishing its identity with another object -- distinguishable even from another object with the same descriptive attributes. An identifying attribute must be guaranteed to be unique within the system however that system is defined -- even if distributed, even when objects are archived."
  • Value Objects
    • "Tracking the identity of entities is essential, but attaching identity to other objects can hurt system performance, add analytical work, and muddle the model by making all objects look the same.

      "Software design is a constant battle with complexity. We must make distinctions so that special handling is applied only where necessary.

      "However, if we think of this category of object as just the absence of identity, we haven't added much to our toolbox or vocabulary. In fact, these objects have characteristics of their own and their own significance to the model. These are the objects that describe things."
    • "When you care only about the attributes of an element of the model, classify it as a value object. Make it express the meaning of the attributes it conveys and give it related functionality. Treat the value objects as immutable. Don't give it any identity and avoid the design complexities necessary to maintain entities."
    • Designing Value Objects
      • "We don't care which instance we have of a value object. This lack of constraints gives us design freedom we can use to simplify the design or optimize performance. This involves making choices about copying, sharing, and immutability."
      • To safely share or copy value objects, they must be immutable.
      • You can choose either to share or to copy, depending on what constraints you are trying to optimize for.
    • Designing Associations that Involve Value Objects
      • Bidirectional associations between two value objects make no sense.
      • Without identity, it is meaningless to say that an object points back to the same value object that points to it.
      • "Try to completely eliminate bidirectional associations between value objects. If in the end such associations seem necessary in your model, rethink the decision to declare the object a value object in the first place. Maybe it has an identity that hasn't been explicitly recognized yet."
  • Services
    • "Some concepts from the domain aren't natural to model as objects. Forcing the required domain functionality to be the responsibility of an entity or value either distorts the definition of a model-based object or adds meaningless artificial objects."
    • A service is an operation offered as an interface that stands alone in the model, without encapsulating state, as entities and value objects do.
    • A good service has three characteristics:
      • The operation relates to a domain concept that is not a natural part of an entity or value object.
      • The interface is defined in terms of other elements of the domain model.
      • The operation is stateless.
    • Services and the Isolated Domain Layer
      • Services are used in other layers than just the domain layer.
      • "It takes care to distinguish services that belong to the domain layer from those of other layers, and to factor responsibilities to keep that distinction sharp."
    • Granularity
      • The service pattern can be used as a mean of controlling granularity in the interfaces of the domain layer.
      • "Medium-grained, stateless services can be easier to reuse in large systems because they encapsulate significant functionality behind a simple interface."
    • Access to Services
      • "Distributed system architectures [...] provide special publishing mechanisms for services, with conventions for their use, and they add distribution and access capabilities. [These] architectures should be used only when there is a real need to distribute the system or otherwise draw on the framework's capabilities."
  • Modules (A.K.A Packages)
    • "Everyone uses modules, but few treat them as a full-fledged part of the model. Code gets broken down into all sorts of categories, from aspects of the technical architecture to developers' work assignments. Even developers who refactor a lot tend to content themselves with modules conceived early in the project.

      "It is a truism that there should be low coupling between modules and high cohesion within them. [...] There is a limit to how many things a person can think about at once (hence low coupling). Incoherent fragments of ideas are as hard to understand as an undifferentiated soup of ideas (hence high cohesion)."
    • "Choose modules that tell the story of the system and contain a cohesive set of concepts."
    • "Give modules names that become part of the ubiquitous language. Modules and their names should reflect insight into the domain."
    • Agile Modules
      • "Modules need to coevolve with the rest of the model. This means refactoring modules right along with the model and code."
      • This refactoring often doesn't happen because there are many difficulties in refactoring modules.
      • "Whatever development technology the implementation will be based on, we need to look for ways of minimizing the work of refactoring modules, and minimizing clutter in communicating to other developers."
    • The Pitfalls of Infrastructure-Driven Packaging
      • Many frameworks encourage package structures that reflect the infrastructure.
      • This can lead to a business object being split across multiple packages, causing low cohesion.
      • This can also lead to packages that have a high number of calls between them, causing high coupling.
      • "Use packaging to separate the domain layer from other code. Otherwise, leave as much freedom as possible to the domain developers to package the domain objects in ways that support their model and design choices."
  • Modeling Paradigms
    • Why the Object Paradigm Predominates
      • Object modeling strikes a nice balance of simplicity and sophistication.
      • It also has significant circumstantial advantages deriving from maturity and widespread adoption.
      • It has a mature developer community.
    • Nonobjects in an Object World
      • "Whatever the dominant model paradigm may be on a project, there are bound to be parts of the domain that would be much easier to express in some other paradigm."
      • "When there are just a few anomalous elements of a domain that otherwise works well in a paradigm, developers can live with a few awkward objects in an otherwise consistent model."
      • "But when major parts of the domain seem to belong to different paradigms, it is intellectually appealing to model each part in a paradigm that fits."
      • "[M]aking a coherent model that spans paradigms is hard, and making the supporting tools coexist is complicated."
    • Sticking with Model-Driven Design When Mixing Paradigms
      • "Without a seamless environment, it falls on the developers to distill a model made up of clear, fundamental concepts to hold the whole design together."
      • "The most effective tool for holding the parts together is a robust ubiquitous language that underlies the whole heterogeneous model."