Partial vs. Full Architectural Boundaries: Strategies and Trade‑offs
The article discusses the high cost of building complete architectural boundaries, explores why architects may deliberately leave room for future boundaries, and presents three practical approaches—incomplete boundaries, one‑way boundaries, and façade patterns—illustrating their benefits, drawbacks, and implementation considerations.
Building a complete architectural boundary is expensive because it requires designing bidirectional polymorphic interfaces, input/output data structures, and all necessary dependency management to isolate components for independent compilation and deployment. This effort entails substantial work and ongoing maintenance.
Often, architects consider the expense too high but still want to reserve space for potential future boundaries.
This anticipatory design is sometimes criticized by the agile community as violating the YAGNI principle, yet architects may foresee scenarios where a partial boundary becomes necessary.
1. Jump to the Last Step
One way to construct an incomplete boundary is to split the system into a series of components that can be compiled and deployed independently, then combine them into a single component. In other words, set up the interfaces and input/output structures, prepare everything, but still compile and deploy them as a single unit.
This incomplete boundary requires the same amount of code and design work as a full boundary but avoids managing multiple components, version tracking, and release overhead. FitNesse originally used this strategy: its web server component could be separated from the wiki and testing parts, allowing the creation of other web‑based applications without requiring users to download multiple JAR files.
FitNesse’s experience also revealed a drawback: over time, the need for a separate web component diminished, and the separation became fragile, with dependencies crossing in undesirable ways, making re‑separation cumbersome.
2. One‑Way Boundary
Mature architectural boundaries use bidirectional interfaces to maintain two‑way isolation, which typically requires ongoing investment to preserve.
Figure 24.1 (illustrated below) shows a simpler structure that temporarily reserves space for a future full boundary, using a classic strategy pattern where the serviceBoundary interface is used by the client and implemented by serviceImpl .
This design lays a foundation for future boundaries. To isolate the client from ServiceImpl , dependency inversion is required. However, without a bidirectional interface, nothing prevents a reverse channel from forming unless developers remain diligent.
3. Facade
A simpler boundary is the Facade pattern (see Figure 24.2). In this case, no dependency inversion is needed; a Facade class lists all services as methods and delegates calls to classes that the client should not access directly.
Note that the client has transitive dependencies on all service classes; a change in any service’s source forces the client to recompile. This structure also makes it easy to create reverse channels.
We have presented three simple ways to implement a partially complete boundary, each with its own costs and benefits. Depending on the context, each method can serve as an alternative to a full boundary, though if the boundary is never realized, each approach may suffer.
One of the architect’s responsibilities is to decide where a future boundary might exist and whether to implement it fully or partially.
*Excerpt adapted from “The Clean Architecture Way”, reproduced with permission from the WeChat public account “Architect Column”.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.