Building high-quality software to solve real-world problems
Max Heinritz
,
.
5 minutes to read
Loop operates at the intersection of two complex domains: logistics and payments. To help us build high-quality software, we follow a development approach called domain-driven design.
Domain-driven design emphasizes the importance of shared abstractions. It encourages software engineers and domain experts to use consistent terminology and mental models when reasoning about a particular domain and its representation in software. Done properly, it enables deeper collaboration, faster iteration, and ultimately better technology.
Domain-driven design requires upfront investment to establish shared abstractions before writing software. There’s a tradeoff here between speed and quality, and most startups prioritize speed. Facebook’s famous motto is “move fast and break things”, not “sit down with experts and properly think things through”.
Different approaches are appropriate for different kinds of startups. Consider the extremes: Twitter launched a few weeks after conception as a side project, while SpaceX launched several years after conception with several hundred full-time employees.
Our view is that domain-driven design makes sense for startups like Loop that are tackling complex problems with in-house expertise, enterprise customers, clear demand, and strong capitalization. It’s less useful for consumer startups or startups operating in unvalidated markets – situations where finding initial product-market fit is an existential concern.
Definitions
Defining a few terms will help illustrate what exactly domain-driven design entails.
A domain is an area of knowledge. It is a flexible concept – domains can be defined at various levels of granularity. For example, supply chain is a large domain that can be divided into the subdomains of purchasing, manufacturing, and transportation. Transportation can be further divided into full truckload, less-than-truckload, ocean, domestic, cross-border, etc. Ocean be broken down further into full container and less-than-container.
A model is a way of thinking about something, sometimes called a mental model.
Putting these together, a domain model is a mental model for reasoning about a particular domain. A common misconception is that a domain model is a particular type of diagram or document. Rather, it is the more abstract way of thinking that underlies these various artifacts.
A domain model includes a ubiquitous language: a set of words that should mean the same thing in software source code, business communication, and everyday conversation. It also includes behavior and processes related to these terms.
There are often several possible models for a given domain: consider the variety of mapping projection techniques used to represent the earth. Useful models are those that are concrete enough to help solve the problems at hand while also being flexible enough to evolve and help solve problems in the future. Which model to use will depend on the problems to solve.
Domain modeling
The process of coming up with a domain model is called domain modeling. It is related to but distinct from the traditional tech functions of engineering, design, and product.
Domain modeling differs from engineering in that it is centered on developing a shared understanding across the entire company rather than technical implementation. It is important that the business team understands and approves of the domain model. It is not important for the business team to understand engineering details.
Domain modeling differs from product and design in that it is more centered on terminology and concepts rather than manifestations of those ideas in user interfaces. In general, product and design care more about specific human interactions, while the domain model is focused on proper behind-the-scenes abstractions and managing complexity as the company grows.
Example: frozen banana stand
Let’s imagine we are building software to support a frozen banana stand. The business sells frozen bananas dipped in chocolate and nuts. Our goal is to develop a domain model that helps us track day-to-day operations.
Some of the key terms could be:
Order - What a customer wants, eg “frozen banana with dark chocolate and almonds”.
Price - The amount of money charged to a customer for a particular order.
Payment - A transfer of money from the customer to the banana stand.
Inventory - The set of food ingredients in stock at the banana stand.
Some of the key processes could be:
Ordering - A customer gives their order to the cashier. The cashier enters the order into a system and the system returns a price. The customer provides payment, and the cashier finalizes the order in the system.
Restocking inventory - When inventory is low, more ingredients are ordered from a supplier.
These are the kinds of concepts that domain experts and software engineers should agree upon before writing software. For example, it would be confusing if the staff used the term “a customer request” instead of “a customer order” and the term “ordering inventory” instead of “restocking inventory”. An “order” could mean different things to different people.
However, even with alignment on the basics, this simple model is ambiguous:
What are customers allowed to order?
What does it mean for the cashier to “finalize” an order? Is that the correct word for that process? What if a customer changes their mind?
What constitutes “low” inventory for restocking?
In what ways does the price depend on the order?
Some ambiguity is ok – if we tried to capture everything, our domain model would become unwieldy. What’s important is that we represent enough to help us solve the problems we care about.
Let’s say that the payment and inventory systems will be bought off the shelf. So our primary focus is ordering and pricing. We can refine our model with the following terms:
Order - What a customer wants, expressed as a list of order preferences.
Order preference - An individual part of a customer order corresponding to a well-defined type and value. For example, the preference for “chocolate” may be “milk”, “dark”, or “none”. And the preference for “nuts” may be “peanuts”, “almonds”, or “none”.
Order preference configuration - Details about a specific kind of preference, including a list of possible options and their prices. For example, the configuration for the preference type “chocolate” could be something like “dark - $1.50” and “light - $3.00”.
Pricing engine - Computes a price given an order structured as a list of order preferences.
Now our model is precise enough to be useful. We can use this to build a variety of software: an automated pricing system, an analytics system for seeing what kinds of preferences are most popular among our customer base, a touch screen ordering system for taking customer orders, etc.
Domain-driven design at Loop
The Loop team includes veteran domain experts and technologists. At previous companies, we’ve seen the compounding returns from investing in strong domain models, and we’ve seen the negative impact of weak domain models on velocity and morale. At Loop, we’ve decided to invest time upfront to properly model key aspects of our domain.
The domain modeling process kicked off in late 2021 with an event storming session at offsite. The whole team used sticky notes to collaboratively outline the key processes and terms in our domain, from client onboarding to conducting freight audits and resolving disputes.
One outcome of event storming was a list of important subdomains to be modeled in detail: transportation network, rate management, freight audit, etc. We met weekly over the following months to focus on particular subdomains.
Each session resulted in a wiki page with terminology and business processes defined. We’ve followed up by validating these with client research sessions and external domain expert consultations. These wikis later served as the basis for our software architecture. Since then, we’ve defined a structured development process for new initiatives that includes a collaborative domain modeling session.
Today, our domain model underlies the company’s product roadmap, software architecture, and go-to-market strategy. We are actively refining the model as we grow and expand the business, and we’re looking for thoughtful technologists and domain experts to help us continue building it out.