Friday, August 28, 2020

Domain Driven Design Chapter 15 Summary

 Chapter 15: Distillation

    • "Distillation is the process of separating the components of a mixture to extract the essence in a form that makes it more valuable and useful."
    • "Strategic distillation of a domain model does all of the following:"
      • "Aids all team members in grasping the overall design of the system and how it fits together"
      • "Facilitates communication by identifying a core model of manageable size to enter the ubiquitous language"
      • "Guides refactoring"
      • "Focuses work on areas of the model with the most value"
      • "Guides outsourcing, use of off-the-shelf components, and decisions about assignments"
  • Core Domain
    • "In designing a large system, there are so many contributing components, all complicated and all absolutely necessary to success, that the essence of the domain model, the real business asset, can be obscured and neglected."
    • "The harsh reality is that not all parts of the design are going to be equally refined. Priorities must be set. To make the domain model an asset, the model’s critical core has to be sleek and fully leveraged to create application functionality. But scarce, highly skilled developers tend to gravitate to technical infrastructure or neatly definable domain problems that can be understood without specialized domain knowledge."
    • "Boil the model down. Find the core domain and provide a means of easily distinguishing it from the mass of supporting model and code. Bring the most valuable and specialized concepts into sharp relief. Make the core small.

      Apply top talent to the core domain, and recruit accordingly. Spend the effort in the core to find a deep model and develop a supple design—sufficient to fulfill the vision of the system. Justify investment in any other part by how it supports the distilled core."
    • Choosing the Core
      • "The core domain you choose depends on your point of view. For example, many applications need a generic model of money that could represent various currencies and their exchange rates and conversions. On the other hand, an application to support currency trading might need a more elaborate model of money, which would be considered part of the core. Even in such a case, there may be a part of the money model that is very generic. As insight into the domain deepens with experience, the distillation process can continue by separating the generic money concepts and retaining only the specialized aspects of the model in the core domain."
      • "One application’s core domain is another application’s generic supporting component."
    • Who Does the Work?
      • "It is essential to [assemble] a team matching up a set of strong developers who have a long-term commitment and an interest in becoming repositories of domain knowledge with one or more domain experts who know the business deeply. Domain design is interesting, technically challenging work when approached seriously, and developers can be found who see it this way."
  • An Escalation of Distillations
    • Distillation techniques ordered by needed effort
      • Domain Vision Statement
      • Highlighted Core
      • Generic Subdomains
      • Cohesive Mechanisms
      • Segregated Core
      • Abstract Core
  • Generic Subdomains
    • "Some parts of the model add complexity without capturing or communicating specialized knowledge. Anything extraneous makes the core domain harder to discern and understand. The model clogs up with general principles everyone knows or details that belong to specialties which are not your primary focus but play a supporting role. Yet, however generic, these other elements are essential to the functioning of the system and the full expression of the model."
    • "Identify cohesive subdomains that are not the motivation for your project. Factor out generic models of these subdomains and place them in separate modules. Leave no trace of your specialties in them.

      Once they have been separated, give their continuing development lower priority than the core domain, and avoid assigning your core developers to the tasks (because they will gain little domain knowledge from them). Also consider off-the-shelf solutions or published models for these generic subdomains."
    • Generic Doesn't Mean Reusable
      • "Note that while I have emphasized the generic quality of these subdomains, I have not mentioned the reusability of code. Off-the-shelf solutions may or may not make sense for a particular situation, but assuming that you are implementing the code yourself, in-house or outsourced, you should specifically not concern yourself with the reusability of that code. This would go against the basic motivation of distillation: that you should be applying as much of your effort to the core domain as possible and investing in supporting generic subdomains only as necessary."
    • Project Risk Management
      • "Projects face risk from both sides, with some projects having greater technical risks and others greater domain modeling risks. The end-to-end system mitigates risk only to the extent that it is an embryonic version of the challenging parts of the actual system. It is easy to underestimate the domain modeling risk. It can take the form of unforeseen complexity, inadequate access to business experts, or gaps in key skills of the developers."
      • "Therefore, except when the team has proven skills and the domain is very familiar, the first-cut system should be based on some part of the core domain, however simple."
  • Domain Vision Statement
    • "At the beginning of a project, the model usually doesn’t even exist, yet the need to focus its development is already there. In later stages of development, there is a need for an explanation of the value of the system that does not require an in-depth study of the model. Also, the critical aspects of the domain model may span multiple bounded contexts, but by definition these distinct models can’t be structured to show their common focus."
    • "Write a short description (about one page) of the core domain and the value it will bring, the “value proposition.” Ignore those aspects that do not distinguish this domain model from others. Show how the domain model serves and balances diverse interests. Keep it narrow. Write this statement early and revise it as you gain new insight."
  • Highlighted Core
    • "Even though team members may know broadly what constitutes the core domain, different people won’t pick out quite the same elements, and even the same person won’t be consistent from one day to the next. The mental labor of constantly filtering the model to identify the key parts absorbs concentration better spent on design thinking, and it requires comprehensive knowledge of the model. The core domain must be made easier to see.

      Significant structural changes to the code are the ideal way of identifying the core domain, but they are not always practical in the short term. In fact, such major code changes are difficult to undertake without the very view the team is lacking."
    • The Distillation Document
      • "Often I create a separate document to describe and explain the core domain. It can be as simple as a list of the most essential conceptual objects. It can be a set of diagrams focused on those objects, showing their most critical relationships. It can walk through the fundamental interactions at an abstract level or by example. It can use UML class or sequence diagrams, nonstandard diagrams particular to the domain, carefully worded textual explanations, or combinations of these. A distillation document is not a complete design document. It is a minimalist entry point that delineates and explains the core and suggests reasons for closer scrutiny of particular pieces. The reader is given a broad view of how the pieces fit and guided to the appropriate part of the code for more details."
    • The Flagged Core
      • "Flag each element of the core domain within the primary repository of the model, without particularly trying to elucidate its role. Make it effortless for a developer to know what is in or out of the core."
    • The Distillation Document as Process Tool
      • "If the distillation document outlines the essentials of the core domain, then it serves as a practical indicator of the significance of a model change. When a model or code change affects the distillation document, it requires consultation with other team members. When the change is made, it requires immediate notification of all team members, and the dissemination of a new version of the document. Changes outside the core or to details not included in the distillation document can be integrated without consultation or notification and will be encountered by other members in the course of their work. Then the developers have the full autonomy that XP suggests."
  • Cohesive Mechanisms
    • "Computations sometimes reach a level of complexity that begins to bloat the design. The conceptual “what” is swamped by the mechanistic “how.” A large number of methods that provide algorithms for resolving the problem obscure the methods that express the problem."
    • "Partition a conceptually cohesive mechanism into a separate lightweight framework. Particularly watch for formalisms or welldocumented categories of algorithms. Expose the capabilities of the framework with an intention-revealing interface. Now the other elements of the domain can focus on expressing the problem (“what”), delegating the intricacies of the solution (“how”) to the framework."
    • Generic Subdomain Versus Cohesive Mechanism
      • "Both generic subdomains and cohesive mechanisms are motivated by the same desire to unburden the core domain. The difference is the nature of the responsibility taken on. A generic subdomain is based on an expressive model that represents some aspect of how the team views the domain. In this it is no different than the core domain, just less central, less important, less specialized. A cohesive mechanism does not represent the domain; it solves some sticky computational problem posed by the expressive models."
    • When a Mechanism is Part of the Core Domain
      • "You almost always want to remove mechanisms from the core domain. The one exception is when a mechanism is itself proprietary and a key part of the value of the software. This is sometimes the case with highly specialized algorithms."
  • Distilling to a Declarative Style
    • " The value of distillation is being able to see what you are doing: cutting to the essence without being distracted by irrelevant detail. Important parts of the core domain may be able to follow a declarative style, when the supporting design provides an economical language for expressing the concepts and rules of the core while encapsulating the means of computing or enforcing them."
  • Segregated Core
    • "Elements in the model may partially serve the core domain and partially play supporting roles. core elements may be tightly coupled to generic ones. The conceptual cohesion of the core may not be strong or visible. All this clutter and entanglement chokes the core. Designers can’t clearly see the most important relationships, leading to a weak design."
    • "Refactor the model to separate the core concepts from supporting players (including ill-defined ones) and strengthen the cohesion of the core while reducing its coupling to other code. Factor all generic or supporting elements into other objects and place them into other packages, even if this means refactoring the model in ways that separate highly coupled elements."
    • The Costs of Creating a Segregated Core
      • "Segregating the core will sometimes make relationships with tightly coupled non-core classes more obscure or even more complicated, but that cost is outweighed by the benefit of clarifying the core domain and making it much easier to work on."
      • "The other cost, of course, is that segregating the core is a lot of work. It must be acknowledged that a decision to go to a segregated core will potentially absorb developers in changes all over the system."
      • "The time to chop out a segregated core is when you have a large bounded context that is critical to the system, but where the essential part of the model is being obscured by a great deal of supporting capability."
    • Evolving Team Decision
      • "As with many strategic design decisions, an entire team must move to a segregated core together. This step requires a team decision process and a team disciplined and coordinated enough to carry out the decision. The challenge is to constrain everyone to use the same definition of the core while not freezing that decision. Because the core domain evolves just like every other aspect of a design, experience working with a segregated core will lead to new insights into what is essential and what is a supporting element. Those insights should feed back into a refined definition of the core domain and of the segregated core modules."
  • Abstract Core
    • "When there is a lot of interaction between subdomains in separate modules, either many references will have to be created between modules, which defeats much of the value of the partitioning, or the interaction will have to be made indirect, which makes the model obscure."
    • "Identify the most fundamental concepts in the model and factor them into distinct classes, abstract classes, or interfaces. Design this abstract model so that it expresses most of the interaction between significant components. Place this abstract overall model in its own module, while the specialized, detailed implementation classes are left in their own modules defined by subdomain."
  • Deep Models Distill
    • "Although a breakthrough to a deep model provides value anywhere it happens, it is in the core domain that it can change the trajectory of an entire project."
  • Choosing Refactoring Targets
    • "In a pain-driven refactoring, you look to see if the root involves the core domain or the relationship of the core to a supporting element. If it does, you bite the bullet and fix that first."
    • When you have the luxury of refactoring freely, you focus first on better factoring of the core domain, on improving the segregation of the core, and on purifying supporting subdomains to be generic.

Monday, August 17, 2020

Domain Driven Design Chapter 14 Summary

 Chapter 14: Maintaining Model Integrity

    • Shares a story of one group trying to reuse the code of a Charge module written by a different group, and how that started breaking that other group's code.
    • "The problem was that these two groups had different models, but they did not realize it, and there were no processes in place to detect it. Each made assumptions about the nature of a charge that were useful in their context (billing customers versus paying vendors). When their code was combined without resolving these contradictions, the result was unreliable software."
    • "If only they had been more aware of this reality, they could have consciously decided how to deal with it. That might have meant working together to hammer out a common model and then writing an automated test suite to prevent future surprises. Or it might simply have meant an agreement to develop separate models and keep hands off each other’s code. Either way, it starts with an explicit agreement on the boundaries within which each model applies."
    • "[T]he most fundamental requirement of a model is that it be internally consistent; that its terms always have the same meaning, and that it contain no contradictory rules. The internal consistency of a model, such that each term is unambiguous and no rules contradict, is called unification. A model is meaningless unless it is logically consistent."
    • "To maintain that level of unification in an entire enterprise system is more trouble than it is worth. It is necessary to allow multiple models to develop in different parts of the system, but we need to make careful choices about which parts of the system will be allowed to diverge and what their relationship to each other will be. We need ways of keeping crucial parts of the model tightly unified. None of this happens by itself or through good intentions. It happens only through conscious design decisions and institution of specific processes. Total unification of the domain model for a large system will not be feasible or cost-effective"
    • "This chapter lays out techniques for recognizing, communicating, and choosing the limits of a model and its relationships to others. It all starts with mapping the current terrain of the project. A bounded context defines the range of applicability of each model, while a context map gives a global overview of the project’s contexts and the relationships between them. This reduction of ambiguity will, in and of itself, change the way things happen on the project, but it isn’t necessarily enough. Once we have a context bounded, a process of continuous integration will keep the model unified.

      Then, starting from this stable situation, we can start to migrate toward more effective strategies for bounding contexts and relating them, ranging from closely allied contexts with shared kernels to loosely coupled models that go their separate ways."
  • Bounded Context
    • "Multiple models are in play on any large project. Yet when code based on distinct models is combined, software becomes buggy, unreliable, and difficult to understand. Communication among team members becomes confused. It is often unclear in what context a model should not be applied."
    • "Explicitly define the context within which a model applies. Explicitly set boundaries in terms of team organization, usage within specific parts of the application, and physical manifestations such as code bases and database schemas. Keep the model strictly consistent within these bounds, but don’t be distracted or confused by issues outside."
    • Recognizing Splinters Within a Bounded Context
      • "Combining elements of distinct models causes two categories of problems: duplicate concepts and false cognates."
      • "Duplication of concepts means that there are two model elements (and attendant implementations) that actually represent the same concept. Every time this information changes, it has to be updated in two places with conversions. Every time new knowledge leads to a change in one of the objects, the other has to be reanalyzed and changed too. Except the reanalysis doesn’t happen in reality, so the result is two versions of the same concept that follow different rules and even have different data. On top of that, the team members must learn not one but two ways of doing the same thing, along with all the ways they are being synchronized."
      • "False cognates may be slightly less common, but more insidiously harmful. This is the case when two people who are using the same term (or implemented object) think they are talking about the same thing, but really are not. [...] False cognates lead to development teams that step on each other’s code, databases that have weird contradictions, and confusion in communication within the team."
      • "When you detect these problems, your team will have to make a decision. You may want to pull the model back together and refine the processes to prevent fragmentation. Or the fragmentation may be a result of groups who want to pull the model in different directions for good reasons, and you may decide to let them develop independently. Dealing with these issues is the subject of the remaining patterns in this chapter."
  • Continuous Integration
    • "Having defined a bounded context, we must keep it sound."
    • "When a number of people are working in the same bounded context, there is a strong tendency for the model to fragment. The bigger the team, the bigger the problem, but as few as three or four people can encounter serious problems. Yet breaking down the system into ever-smaller contexts eventually loses a valuable level of integration and coherency."
    • "Continuous integration means that all work within the context is being merged and made consistent frequently enough that when splinters happen they are caught and corrected quickly. continuous integration, like everything else in domain-driven design, operates at two levels: (1) the integration of model concepts and (2) the integration of the implementation."
    • "Concepts are integrated by constant communication among team members. The team must cultivate a shared understanding of the ever-changing model. Many practices help, but the most fundamental is constantly hammering out the ubiquitous language. Meanwhile, the implementation artifacts are being integrated by a systematic merge/build/test process that exposes model splinters early. Many processes for integration are used, but most of the effective ones share these characteristics:"
      • A step-by-step, reproducible merge/build technique
      • Automated test suites
      • Rules that set some reasonably small upper limit on the lifetime of unintegrated changes
    • "Institute a process of merging all code and other implementation artifacts frequently, with automated tests to flag fragmentation quickly. Relentlessly exercise the ubiquitous language to hammer out a shared view of the model as the concepts evolve in different people’s heads."
  • Context Map
    • "An individual bounded context still does not provide a global view. The context of other models may still be vague and in flux."
    • "People on other teams won’t be very aware of the context bounds and will unknowingly make changes that blur the edges or complicate the interconnections. When connections must be made between different contexts, they tend to bleed into each other."
    • "Identify each model in play on the project and define its bounded context. This includes the implicit models of nonobject-oriented subsystems. Name each bounded context, and make the names part of the ubiquitous language.
      Describe the points of contact between the models, outlining explicit translation for any communication and highlighting any sharing.
      Map the existing terrain. Take up transformations later."
    • Testing at the Context Boundaries
      • "Contact points with other bounded contexts are particularly important to test. Tests help compensate for the subtleties of translation and the lower level of communication that typically exist at boundaries. They can act as a valuable early warning system, especially reassuring in cases where you depend on the details of a model you don’t control."
    • Organizing and Documenting Context Maps
      • "The bounded contexts should have names so that you can talk about them. Those names should enter the ubiquitous language of the team."
      • "Everyone has to know where the boundaries lie, and be able to recognize the context of any piece of code or any situation."
  • Relationships Between Bounded Contexts
    • "The following patterns cover a range of strategies for relating two models that can be composed to encompass an entire enterprise. These patterns serve the dual purpose of providing targets for successfully organizing development work, and supplying vocabulary for describing the existing organization."
  • Shared Kernel
    • "Uncoordinated teams working on closely related applications can go racing forward for a while, but what they produce may not fit together. They can end up spending more on translation layers and retrofitting than they would have on continuous integration in the first place, meanwhile duplicating effort and losing the benefits of a common ubiquitous language."
    • "Designate some subset of the domain model that the two teams agree to share. Of course this includes, along with this subset of the model, the subset of code or of the database design associated with that part of the model. This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team.

      Integrate a functional system frequently, but somewhat less often than the pace of continuous integration within the teams. At these integrations, run the tests of both teams."
  • Customer/Supplier Development Teams
    • "Often one subsystem essentially feeds another; the “downstream” component performs analysis or other functions that feed back very little into the “upstream” component, and all dependencies go one way. The two subsystems commonly serve very different user communities, who do different jobs, where different models may be useful. The tool set may also be different, so that program code cannot be shared."
    • "The freewheeling development of the upstream team can be cramped if the downstream team has veto power over changes, or if procedures for requesting changes are too cumbersome. The upstream team may even be inhibited, worried about breaking the downstream system. Meanwhile, the downstream team can be helpless, at the mercy of upstream priorities."
    • "Establish a clear customer/supplier relationship between the two teams. In planning sessions, make the downstream team play the customer role to the upstream team. Negotiate and budget tasks for downstream requirements so that everyone understands the commitment and schedule.
      Jointly develop automated acceptance tests that will validate the interface expected. Add these tests to the upstream team’s test suite, to be run as part of its continuous integration. This testing will free the upstream team to make changes without fear of side effects downstream."
    • There are two crucial elements to this pattern
      • " The relationship must be that of customer and supplier, with the implication that the customer’s needs are paramount. Because the downstream team is not the only customer, the different customers’ demands have to be balanced in negotiation—but they remain priorities. This situation is in contrast to the poor-cousin relationship that often emerges, in which the downstream team has to come begging to the upstream team for its needs."
      • "There must be an automated test suite that allows the upstream team to change its code without fear of breaking the downstream, and lets the downstream team concentrate on its own work without constantly monitoring the upstream team."
  • Conformist
    • "When two development teams have an upstream/downstream relationship in which the upstream has no motivation to provide for the downstream team’s needs, the downstream team is helpless. Altruism may motivate upstream developers to make promises, but they are unlikely to be fulfilled. Belief in those good intentions leads the downstream team to make plans based on features that will never be available. The downstream project will be delayed until the team ultimately learns to live with what it is given. An interface tailored to the needs of the downstream team is not in the cards."
    • "In this situation, there are three possible paths."
      • Separate Ways - "One is to abandon use of the upstream altogether. This option should be evaluated realistically, making no assumptions that the upstream will accommodate downstream needs."
      • Anticorruption Layer - "If the design is very difficult to work with, perhaps for lack of encapsulation, awkward abstractions, or modeling in a paradigm the team cannot use, then the downstream team will still need to develop its own model. They will have to take full responsibility for a translation layer that is likely to be complex."
      • Conformist - "On the other hand, if the quality is not so bad, and the style is reasonably compatible, then it may be best to give up on an independent model altogether."
    • "Eliminate the complexity of translation between bounded contexts by slavishly adhering to the model of the upstream team. Although this cramps the style of the downstream designers and probably does not yield the ideal model for the application, choosing conformity enormously simplifies integration. Also, you will share a ubiquitous language with your supplier team. The supplier is in the driver’s seat, so it is good to make communication easy for them. Altruism may be sufficient to get them to share information with you. "
  • Anticorruption Layer
    • "When a new system is being built that must have a large interface with another, the difficulty of relating the two models can eventually overwhelm the intent of the new model altogether, causing it to be modified to resemble the other system’s model, in an ad hoc fashion. The models of legacy systems are usually weak, and even the exception that is well developed may not fit the needs of the current project. Yet there may be a lot of value in the integration, and sometimes it is an absolute requirement."
    • "Create an isolating layer to provide clients with functionality in terms of their own domain model. The layer talks to the other system through its existing interface, requiring little or no modification to the other system. Internally, the layer translates in both directions as necessary between the two models."
    • Designing the Interface of the Anticorruption Layer
      • "The public interface of the anticorruption layer usually appears as a set of services, although occasionally it can take the form of an entity. Building a whole new layer responsible for the translation between the semantics of the two systems gives us an opportunity to reabstract the other system’s behavior and offer its services and information to our system consistently with our model. It may not even make sense, in our model, to represent the external system as a single component. It may be best to use multiple services (or occasionally entities), each of which has a coherent responsibility in terms of our model."
    • Implementing the Anticorruption Layer
      • "One way of organizing the design of the anticorruption layer is as a combination of facades, adapters (both from Gamma et al. 1995), and translators, along with the communication and transport mechanisms usually needed to talk between systems."
      • "We often have to integrate with systems that have large, complicated, messy interfaces. [...] Translating from one model to another (especially if one model is fuzzy) is a hard enough job without simultaneously dealing with a subsystem interface that is hard to talk to. Fortunately, that is what facades are for. A facade is an alternative interface for a subsystem that simplifies access for the client and makes the subsystem easier to use."
      • "An adapter is a wrapper that allows a client to use a different protocol than that understood by the implementer of the behavior. When a client sends a message to an ADAPTER, it is converted to a semantically equivalent message and sent on to the “adaptee.” The response is converted and passed back. I’m using the term adapter a little loosely, because the emphasis in Gamma et al. 1995 is on making a wrapped object conform to a standard interface that clients expect, whereas we get to choose the adapted interface, and the adaptee is probably not even an object. Our emphasis is on translation between two models, but I think this is consistent with the intent of adapter."
      • "The remaining element is the translator. The adapter's job is to know how to make a request. The actual conversion of conceptual objects or data is a distinct, complex task that can be placed in its own object, making them both much easier to understand. A translator can be a lightweight object that is instantiated when needed. It needs no state and does not need to be distributed, because it belongs with the adapters(s) it serves."
    • A Cautionary Tale
  • Separate Ways
    • "Integration is always expensive. Sometimes the benefit is small."
    • "Declare a bounded context to have no connection to the others at all, allowing developers to find simple, specialized solutions within this small scope."
  • Open Host Service
    • "When a subsystem has to be integrated with many others, customizing a translator for each can bog down the team. There is more and more to maintain, and more and more to worry about when changes are made."
    • "Define a protocol that gives access to your subsystem as a set of services. Open the protocol so that all who need to integrate with you can use it. Enhance and expand the protocol to handle new integration requirements, except when a single team has idiosyncratic needs. Then, use a one-off translator to augment the protocol for that special case so that the shared protocol can stay simple and coherent."
  • Published Language
    • "When businesses want to exchange information with one another, how do they do it? Not only is it unrealistic to expect one to adopt the domain model of the other, it may be undesirable for both parties. A domain model is developed to solve problems for its users; such a model may contain features that needlessly complicate communication with another system. Also, if the model underlying one of the applications is used as the communications medium, it cannot be changed freely to meet new needs, but must be very stable to support the ongoing communication role."
    • "Direct translation to and from the existing domain models may not be a good solution. Those models may be overly complex or poorly factored. They are probably undocumented. If one is used as a data interchange language, it essentially becomes frozen and cannot respond to new development needs."
    • "Use a well-documented shared language that can express the necessary domain information as a common medium of communication, translating as necessary into and out of that language."
  • Unifying an Elephant
    • Starts off by quoting parts of "The Blind Men and the Elephant," by John Godfrey Saxe, where each blind man compares the elephant to something different depending on what part they touched: a wall, a snake, a tree, a rope, etc.
    • "Depending on their goals in interacting with the elephant, the various blind men may still be able to make progress, even if they don’t fully agree on the nature of the elephant. If no integration is required, then it doesn’t matter that the models are not unified. If they require some integration, they may not actually have to agree on what an elephant is, but they will get a lot of value from merely recognizing that they don’t agree. This way, at least they don’t unknowingly talk at cross-purposes."
    • "As the blind men want to share more information about the elephant, the value of sharing a single bounded context goes up. But unifying the disparate models is a challenge. None of them is likely to give up his model and adopt one of the others. After all, the man who touched the tail knows the elephant is not like a tree, and that model would be meaningless and useless to him. Unifying multiple models almost always means creating a new model."
    • "With some imagination and continued discussion (probably heated), the blind men could eventually recognize that they have been describing and modeling different parts of a larger whole. For many purposes, a part-whole unification may not require much additional work. At least the first stage of integration only requires figuring out how the parts are related. It may be adequate for some needs to view an elephant as a wall, held up by tree trunks, with a rope at one end and a snake at the other."
    • "Matters are more difficult when two models are looking at the same part in a different way. If two men had touched the trunk and one described it as a snake and the other described it as a fire hose, they would have had more difficulty. Neither can accept the other’s model, because it contradicts his own experience. In fact, they need a new abstraction that incorporates the “aliveness” of a snake with the water-shooting functionality of a fire hose, but one that leaves out the inapt implications of the first models, such as the expectation of possibly venomous fangs, or the ability to be detached from the body and rolled up into a compartment in a fire truck."
    • "Even though we have combined the parts into a whole, the resulting model is crude. It is incoherent, lacking any sense of following contours of an underlying domain. New insights could lead to a deeper model in a process of continuous refinement. New application requirements can also force the move to a deeper model. If the elephant starts moving, the “tree” theory is out, and our blind modelers may break through to the concept of “legs.”"
    • "This second pass of model integration tends to slough off incidental or incorrect aspects of the individual models and creates new concepts—in this case, “animal” with parts “trunk,” “leg,” “body,” and “tail”—each of which has its own properties and clear relationships to other parts. Successful model unification, to a large extent, hinges on minimalism. An elephant trunk is both more and less than a snake, but the “less” is probably more important than the “more.” Better to lack the water-spewing ability than to have an incorrect poison-fang feature."
  • Choosing Your Model Context Strategy
    • "It is important always to draw the CONTEXT MAP to reflect the current situation at any given time. Once that’s done, though, you may very well want to change that reality. Now you can begin to consciously choose CONTEXT boundaries and relationships. Here are some guidelines."
    • Team Decision or Higher
      • "Teams have to make these decisions, or at least the decisions have to be propagated to the entire team and understood by everyone. In fact, such decisions often involve agreements beyond your own team. On the merits, decisions about whether to expand or to partition bounded contexts should be based on the cost-benefit trade-off between the value of independent team action and the value of direct and rich integration. In practice, political relationships between teams often determine how systems are integrated"
    • Putting Ourselves in Context
      • "When we are working on a software project, we are interested primarily in the parts of the system our team is changing (the “system under design”) and secondarily in the systems it will communicate with. [...] We really are part of that primary context we are working in, and that is bound to be reflected in our context. This isn’t a problem if we are aware of the bias and are mindful of when we step outside the limits of that map’s applicability."
    • Transforming Boundaries
      • "[T]ypically the struggle is to balance some subset of the following forces:"
        • Favoring Larger Bounded Contexts
          • Flow between user tasks is smoother when more is handled with a unified model.
          • It is easier to understand one coherent model than two distinct ones plus mappings
          •  Translation between two models can be difficult (sometimes impossible).
          • Shared language fosters clear team communication.
        • Favoring Smaller Bounded Contexts
          • Communication overhead between developers is reduced.
          • Continuous integration is easier with smaller teams and code bases.
          • Larger contexts may call for more versatile abstract models, requiring skills that are in short supply.
          • Different models can cater to special needs or encompass the jargon of specialized groups of users, along with specialized dialects of the ubiquitous language.
    • Accepting That Which We Cannot Change: Delineating the External Systems
      • "Some subsystems will clearly not be in any BOUNDED CONTEXT of the system under development. Examples would be major legacy systems that you are not immediately replacing and external systems that provide services you’ll need. You can identify these immediately and prepare to segregate them from your design."
      • "Here we must be careful about our assumptions.It is not unusual to find semantic contradictions in different parts of such systems."
    • Relationships with the External Systems
      • "There are three patterns that can apply here. First, to consider separate ways. Yes, you wouldn’t have included them if you didn’t need integration. But be really sure. Would it be sufficient to give the user easy access to both systems? Integration is expensive and distracting, so unburden your project as much as you can."
      • "If the integration is really essential, you can choose between two extremes: conformist or anticorruption layer."
    • The System Under Design
      • "The software your project team is actually building is the system under design. You can declare bounded contexts within this zone and apply continuous integration within each to keep them unified. But how many should you have? What relationships should they have to each other? The answers are less cut and dried than with the external systems because we have more freedom and control."
    • Catering to Special Needs with Distinct Models
      • "Different groups within the same business have often developed their own specialized terminologies, which may have diverged from one another. These local jargons may be very precise and tailored to their needs. Changing them (for example, by imposing a standardized, enterprise-wide terminology) requires extensive training and analysis to resolve the differences. Even then, the new terminology may not serve as well as the finely tuned version they already had."
      • "You may decide to cater to these special needs in separate bounded contexts, allowing the models to go separate ways, except for continuous integration of translation layers. Different dialects of the ubiquitous language will evolve around these models and the specialized jargon they are based on. If the two dialects have a lot of overlap, a shared kernel may provide the needed specialization while minimizing the translation cost."
    • Deployment
      • "Coordinating the packaging and deployment of complex systems is one of those boring tasks that are almost always a lot harder than they look. The choice of bounded context strategy has an impact on the deployment. For example, when customer/supplier teams deploy new versions, they have to coordinate with each other to release versions that have been tested together. Both code and data migrations have to work in these combinations. In a distributed system, it may help to keep the translation layers between contexts together within a single process, so that you don’t have multiple versions coexisting.

        Even deployment of the components of a single bounded context can be challenging when data migration takes time or when distributed systems can’t be updated instantaneously, resulting in two versions of the code and data coexisting."
    • The Trade-off
      • "To sum up these guidelines, there is a range of strategies for unifying or integrating models. In general terms, you will trade off the benefits of seamless integration of functionality against the additional effort of coordination and communication. You trade more independent action against smoother communication. More ambitious unification requires control over the design of the subsystems involved."
    • When Your Project Is Already Under Way
      • "Most likely, you are not starting a project but are looking to improve a project that is already under way. In this case, the first step is to define BOUNDED CONTEXTS according to the way things are now."
      • "Once you have delineated your true current bounded contexts and described the relationships they currently have, the next step is to tighten up the team’s practices around that current organization. Improve your continuous integration within the contexts. Refactor any stray translation code into your anticorruption layers. Name the existing bounded contexts and make sure they are in the ubiquitous language of the project."
      • "Now you are ready to consider changes to the boundaries and relationships themselves. These changes will naturally be driven by the same principles I’ve already described for a new project, but they will have to be bitten off in small pieces, chosen pragmatically to give the most value for the least effort and disruption."
  • Transformations
    • "Like any other aspect of modeling and design, decisions about bounded contexts are not irrevocable. Inevitably, there will be many cases in which you have to change your initial decision about the boundaries and relationships between bounded contexts."
    • Merging Contexts: Separate Ways -> Shared Kernel
      • "This is hard to do. It’s not too late, but it takes some patience."
      • "Evaluate the initial situation. Be sure that the two contexts are indeed internally unified before beginning to unify them with each other."
      • "Set up the process. You’ll need to decide how the code will be shared and what the module naming conventions will be. There must be at least weekly integration of the shared kernel code. And it must have a test suite. Set this up before developing any shared code. (The test suite will be empty, so it should be easy to pass!)"
      • "Choose some small subdomain to start with—something duplicated in both contexts, but not part of the core domain. This first merger is going to establish the process, so it is best to use something simple and relatively generic or noncritical. Examine the integrations and translations that already exist. Choosing something that is being translated has the advantage of starting out with a proven translation, plus you’ll be thinning your translation layer."
      • "Form a group of two to four developers, drawn from both teams, to work out a shared model for the subdomain. Regardless of how the model is derived, it must be ironed out in detail. This includes the hard work of identifying synonyms and mapping any terms that are not already being translated. This joint team outlines a basic set of tests for the model."
      • "Developers from either team take on the task of implementing the model (or adapting existing code to be shared), working out details and making it function. If these developers run into problems with the model, they reconvene the team from step 3 and participate in any necessary revisions of the concepts."
      • "Developers of each team take on the task of integrating with the new shared kernel."
      • "Remove translations that are no longer needed."
    • Merging Contexts: Shared Kernel -> Continuous Integration
      • "This is not just a matter of resolving the model differences. You are going to be changing team structures and ultimately the language people speak."
      • "Be sure that all the processes needed for continuous integration (shared code ownership, frequent integration, and so on) are in place on each team, separately. Harmonize integration procedures on the two teams so that everyone is doing things in the same way."
      • "Start circulating team members between teams. This will create a pool of people who understand both models, and will begin to connect the people of the two teams."
      • "Clarify the distillation of each model individually."
      • "At this point, confidence should be high enough to begin merging the core domain into the shared kernel. This can take several iterations, and sometimes temporary translation layers are needed between the newly shared parts and the not-yet-shared parts. Once into merging the core domain, it is best to go pretty fast. It is a high-overhead phase, fraught with errors, and should be shortened as much as possible, taking priority over most new development. But don’t take on more than you can handle."
      • "As the shared kernel grows, increase the integration frequency to daily and finally to continuous integration."
      • "As the shared kernel approaches the point of encompassing all of the two former bounded contexts, you will find yourself with either one large team or two smaller teams that have a shared code base that they integrate continuously, and that trade members back and forth frequently."
    • Phasing Out a Legacy System
      • "Identify specific functionality of the legacy that could be added to one of the favored systems within a single iteration."
      • " Identify additions that will be required in the anticorruption layer."
      • "Implement."
      • "Deploy."
      • "Identify any unnecessary parts of the anticorruption layer and remove them."
      • "Consider excising the now-unused modules of the legacy system, though this may not turn out to be practical. Ironically, the better designed the legacy system is, the easier it will be to phase it out. But badly designed software is hard to dismantle a little at a time. It may be possible to just ignore the unused parts until a later time when the remainder has been phased out and the whole thing can be switched off."
    • Open Host Service -> Published Language
      • " If an industry-standard language is available, evaluate it and use it if at all possible."
      • "If no standard or prepublished language is available, then begin by sharpening up the core domain of the system that will serve as the host."
      • "Use the core domain as the basis of an interchange language, using a standard interchange paradigm such as XML, if at all possible."
      • " Publish the new language to all involved in the collaboration (at least)."
      • "If a new system architecture is involved, publish that too."
      • "Build translation layers for each collaborating system."
      • "Switch over."

Friday, August 7, 2020

Domain Driven Design Chapter 13 Summary

 Chapter 13: Refactoring Toward Deeper Insight

    • "Refactoring toward deeper insight is a multifaceted process. It will be helpful to stop for a moment to pull together the major points. There are three things you have to focus on."
      • Live in the domain.
      • Keep looking at things a different way.
      • Maintain an unbroken dialog with domain experts.
  • Initiation
    • "Refactoring toward deeper insight can begin in many ways. It may be a response to a problem in the code—some complexity or awkwardness. Rather than apply a standard transformation of the code, the developers sense that the root of the problem is in the domain model. Perhaps a concept is missing. Maybe some relationship is wrong."
    • "Seeing the trouble spot is often the hardest and most uncertain part."
  • Exploration Teams
    • "Whatever the source of dissatisfaction, the next step is to seek a refinement that will make the model communicate clearly and naturally. This might require only some modest change that is immediately evident and can be accomplished in a few hours. In that case, the change resembles traditional refactoring. But the search for a new model may well call for more time and the involvement of more people."
    • Keys to keeping the process productive:
      • "Self-determination. A small team can be assembled on the fly to explore a design problem. The team can operate for a few days and then disband. There is no need for long-term, elaborate organizational structures."
      • "Scope and sleep. Two or three short meetings spaced out over a few days should produce a design worth trying. Dragging it out doesn’t help. If you get stuck, you may be taking on too much at once. Pick a smaller aspect of the design and focus on that."
      • "Exercising the ubiquitous language. Involving the other team members—particularly the subject matter expert—in the brainstorming session creates an opportunity to exercise and refine the ubiquitous language. The end result of the effort is a refinement of that language which the original developer(s) will take back and formalize in code."
  • Prior Art
    • "It isn't always necessary to reinvent the wheel"
    • "You can get ideas from books and other sources of knowledge about the domain itself. Although the people in the field may not have created a model suitable for running software, they may well have organized the concepts and found some useful abstractions. Feeding the knowledge-crunching process this way leads to richer, quicker results that also will probably seem more familiar to domain experts."
    • "Sometimes you can draw on the experience of others in the form of analysis patterns. This kind of input has some of the effect of reading about the domain, but in this case it is geared specifically toward software development, and it should be based directly on experience implementing software in your domain. Analysis patterns can give you subtle model concepts and help you avoid lots of mistakes. But they don’t give you a cookbook recipe. They feed the knowledgecrunching process."
  • A Design for Developers
    • "Software isn’t just for users. It’s also for developers. [...] Refactoring toward deeper insight both leads to and benefits from a supple design."
    • "A supple design communicates its intent. The design makes it easy to anticipate the effect of running code—and therefore it easy to anticipate the consequences of changing it. A supple design helps limit mental overload, primarily by reducing dependencies and side effects. "
  • Timing
    • "If you wait until you can make a complete justification for a change, you’ve waited too long."
    • "Continuous refactoring has come to be considered a “best practice,” but most project teams are still too cautious about it. They see the risk of changing code and the cost of developer time to make a change; but what’s harder to see is the risk of keeping an awkward design and the cost of working around that design. Developers who want to refactor are often asked to justify the decision. Although this seems reasonable, it makes an already difficult thing impossibly difficult, and tends to squelch refactoring (or drive it underground). Software development is not such a predictable process that the benefits of a change or the costs of not making a change can be accurately calculated."
    • Refactor when:
      • The design does not express the team’s current understanding of the domain
      • Important concepts are implicit in the design (and you see a way to make them explicit)
      • You see an opportunity to make some important part of the design suppler.
    • "This aggressive attitude does not justify any change at any time. Don’t refactor the day before a release. Don’t introduce “supple designs” that are just demonstrations of technical virtuosity but fail to cut to the core of the domain. Don’t introduce a “deeper model” that you couldn’t convince a domain expert to use, no matter how elegant it seems. Don’t be absolute about things, but push beyond the comfort zone in the direction of favoring refactoring."
  • Crisis as Opportunity
    • "For over a century after Charles Darwin introduced it, the standard model of evolution was that species changed gradually, somewhat steadily, over time. Suddenly, in the 1970s, this model was displaced by the “punctuated equilibrium” model. In this expanded view of evolution, long periods of gradual change or stability are interrupted by relatively short bursts of rapid change. Then things settle down into a new equilibrium. Software development has an intentional direction behind it that evolution lacks (although it may not be evident on some projects), but nonetheless it follows this kind of rhythm."
    • "Such a situation often does not look like an opportunity; it seems more like a crisis. Suddenly there is some obvious inadequacy in the model. There is a gaping hole in what it can express, or some critical area where it is opaque. Maybe it makes statements that are just wrong."

      "This means the team has reached a new level of understanding. From their now-elevated viewpoint, the old model looks poor. From that viewpoint, they can conceive a far better one."

Domain Driven Design Chapter 12 Summary

 Chapter 12: Relating Design Patterns to the Model

    • "Some, not all, of the patterns in Design Patterns can be used as domain patterns. Doing so requires a shift in emphasis. Design Patterns presents a catalog of design elements that have solved problems commonly encountered in a variety of contexts. The motivations of these patterns and the patterns themselves are presented in purely technical terms. But a subset of these elements can be applied in the broader context of domain modeling and design, because they correspond to general concepts that emerge in many domains."
    • "A sample of specific patterns from Design Patterns will show how a pattern conceived as a design pattern can be applied in the domain model, and it will clarify the distinction between a technical design pattern and a domain pattern."
  • Strategy (A.K.A. Policy)
    • "Domain models contain processes that are not technically motivated but actually meaningful in the problem domain. When alternative processes must be provided, the complexity of choosing the appropriate process combines with the complexity of the multiple processes themselves, and things get out of hand."
    • "Factor the varying part of a process into a separate “strategy” object in the model. Factor apart a rule and the behavior it governs. Implement the rule or substitutable process following the strategy design pattern. Multiple versions of the strategy object represent different ways the process can be done."
  • Composite
    • "When the relatedness of nested containers is not reflected in the model, common behavior has to be duplicated at each level of the hierarchy, and nesting is rigid (for example, containers can’t usually contain other containers at their own level, and the number of levels is fixed). Clients must deal with different levels of the hierarchy through different interfaces, even though there may be no conceptual difference they care about. Recursion through the hierarchy to produce aggregated information is very complicated."
    • "Define an abstract type that encompasses all members of the composite. Methods that return information are implemented on containers to return aggregated information about their contents. “Leaf” nodes implement those methods based on their own values. Clients deal with the abstract type and have no need to distinguish leaves from containers."
  • Why Not Flyweight?
    • "[F]lyweight is a good example of a design pattern that has no correspondence to the domain model."
    • "When a limited set of value objects is used many times (as in the example of electrical outlets in a house plan), it may make sense to implement them as flyweights. This is an implementation option available for value objects and not for entities. Contrast this with composite, in which conceptual objects are composed of other conceptual objects. In that case, the pattern applies to both model and implementation, which is an essential trait of a domain pattern."
  • This list is not exhaustive, but is given as an example.

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