Most developers have experienced it before… the dreaded last 10%. At the start, you blast through the initial planning… most entities and processes are seemingly-well defined. The middle goes the usual speed with some ups and downs. Then, the end is near, and you just can’t seem to get there. Things start to slide, and that last 10% ends up taking the majority of the time.
Even with good tests to handle technical problems, it’s easy to back yourself into a fully painted application with a few major problems. Why does this happen?
We begin mapping our domain onto a simplified model, using tools like whiteboards, or some other easily moldable medium. Then, as soon as we are feeling comfortable, we begin to convert that mental model into something our application can use. This may be database schemas, ORM mappings, or perhaps just plain code. We figure that we have enough of the model figured out that we can begin to write our application. So we do. We write layers and layers on top of that incomplete and probably incorrect model. We built our dependencies in the wrong order.
Let’s look at the classic Domain Driven Design structure as an example, which essentially is composed of four layers: User Interface, Application, Domain and Infrastructure. Domain is our business logic. Application layer, optionally, wraps or packages the domain layer and acts as a more thorough service layer. User Interface translates input/output, allowing those services to be called from the another medium. The infrastructure layer implements or assists the domain layer with access to external dependencies. Even ignoring DDD for a moment, this is common, but perhaps your boundaries aren’t quite as obvious. Making boundaries obvious really helps keep responsibilities and dependencies in the right places.
With an application built on incorrect assumptions and incorrect data, it’s very easy to fall into the trap of fixing problems one at a time. Identify problem, identify solution, implement changes across entire system. Rinse and repeat as the incorrect model is slowly chipped away at. Not only is the feedback loop this way extremely slow (hours? days?), it’s like trying to plug holes in a leaky boat. Building a boat that is watertight is much easier before it’s in the water. And often, fixing issues this way leads to new issues because you’re just reacting to changes, rather than driving them.
How can this be avoided?
Write your domain layer, in its complete entirety, first. Don’t think about databases or even HTTP until this is done and thoroughly tested both in terms of test coverage and business cases. Define and create all of your use cases, and make sure your domain model holds up. Your first user interface that you build should be programmatically running through all of your use case objects. Tools like Cucumber or Behat are great for this.
One complimentary architectural pattern here is Entity Boundary Interactor from Uncle Bob. EBI essentially states that each use case is implemented with an Interactor. It works with business objects to perform some task. The interactor has a designed request object and response object, relevant to that interaction. Each user interface (or the user interface), is created with Boundaries. The boundary could be HTTP, command line; it doesn’t matter. For a HTTP interface, its job would be to convert HTTP requests to business requests, and business responses back to HTTP responses. On the other end of this, things like database access are also implemented with boundaries, translating requests for information back into business objects.
With this in place, implementing a real user interface and a real database should relatively easy and predictable, giving you a linear path all the way to the finish line. Until every use case is implemented and fully tested, it might as well be wrong.