Architecture must support the system's primary use cases
The intent and behavior of the system should be clear from the architecture
Developers should not have to search for core behaviors
Key behaviors should be visible at the top level of the system
Components should have descriptive names
Components should be positioned appropriately within the architecture
Operation
Architecture must support operational requirements like high throughput and fast response times
Different architectures suit different needs: parallel services, lightweight threads, isolated processes or monoliths
A flexible architecture should not assume specific communication means, allowing easier transitions between threads, processes and services
Good architecture leaves options open to adjust for changing operational needs
Development
Architecture should support the development environment, allowing team independence
Conway's Law: Any organization that designs a system will produce a design whose structure is a copy of the organization’s communication structure.
System structure mirrors the organization’s communication
Systems with many teams need an architecture that prevents development interference
Properly partitioned components enable isolated, independent work across teams
Deployment
Architecture should simplify and streamline deployment, aiming for "immediate deployment"
Avoids complex configuration scripts, manual setups and tweaks
Proper partitioning and isolation support seamless deployment
Master components should manage integration and supervision for smooth startup
Leaving Options Open
Good architecture balances use cases, operations, team structure and deployment
Realistically, achieving this balance is challenging due to unknown use cases, operational constraints and other changing requirements
Some architectural principles are relatively inexpensive to implement and balance these concerns
Allows us to create well-isolated components to keep options open
The goal of a good architecture is to create a flexible system that is easy to change as needed
Decoupling Layers
The architect won't know all the use cases, but knows the basic intent of the system
Employ Single Responsibility and Common Closure Principle to separate things that change for different reasons and collect things that change for the same reason
UI and business rules should be separate to allow independent changes
Different types of business rules (application-specific vs. domain-wide) should be isolated since they change at different rates for different reasons
Database and technical details should also be independent, as they change for separate reasons
After decoupling the system, it will be divided into horizontal layers (UI, app-specific rules, domain rules, database)
Decoupling Use Cases
Previously looked at horizontal layers of the system
Use cases are also subject to change for different reasons, making them a natural way to divide the system
Separated as vertical slices through the horizontal layers
Each use case should have its own UI, business rules and database functionality
Decoupling allows adding new use cases without affecting existing ones
Group UI and database elements by use case to minimise interference across use cases
Decoupling Mode
Decoupling use cases supports operational needs, such as high and low throughput separation
UI, database and business rules can be deployed on separate servers if decoupled
Independent components can become services or microservices, enabling network-based communication.
Service-oriented architecture (SoA) is one possible approach, but flexibility in decoupling mode is key
A good architecture keeps options open, allowing operational adjustments when needed
Decoupling mode is one of those options
Independent Develop-Ability
Decoupling reduces team interference, allowing independent work on different components
Ex. If business rules don't know much about the UI, then work on the UI cannot really affect a team that focuses on business rules
Decoupled use cases enables teams to work on different use cases without interference (ex. addOrder vs deleteOrder)
As long as the layers and use cases are decoupled, the arhictecture of the system will support all the organisation's teams
Independent Deployability
Decoupling layers and use cases enhances deployment flexibility
Enables independent deployment, allowing hot-swapping of components
New use cases can be added without impacting existing parts of the system
Duplication
Distinguish between true duplication (same change needed in all instances) and accidental duplication (divergent changes over time)
Avoid merging use cases with similar structures prematurely as they may evolve differently
Unifying them early can cause separation to be challenging later
Resist coupling use cases or layers due to apparent duplication in screens, algorithms or database structure
Ensure the duplication is real first
Maintain separate view models for UI and database layers to ensure proper decoupling, even if they appear similar
Decoupling Modes (Again)
Decoupling can occur at various levels: source code, deployment or service level
Source level: Components communicate within a single address space using simple function calls (monolithic)
Deployment level: Components are deployable units (ex. DLLs, jar files) that can operate independently
Service level: Components operate as independent services, communicating via network packets
Optimal decoupling mode may change as a project progresses, so the architecture should remain flexible
Decoupling at the service-level by default is expensive and encourages coarse-grained decoupling
A solution is to push decoupling to the point where a service could be formed, leaving components in the same address space for as long as possible
Leaving the option for a service open
Good architectures allows transitions between monolith and microservices based on evolving operational needs
Conclusion
Changing decoupling mode should be feasible as the system evolves
The decoupling mode may need to change over time depending on how the system grows
A good architect anticipates and enables flexibility in decoupling