SE 07 Software Architecture - hmislk/hmis GitHub Wiki
SE-07: Software Architecture
Part of the Software Engineering Principles series
What Is Software Architecture?
Software architecture is the high-level structure of a software system β the set of significant decisions about how the system is decomposed into components, how those components interact, and what principles guide those decisions.
Architecture is distinct from implementation in that architectural decisions are:
- Difficult to reverse once the system is built
- Systemic β they affect the entire codebase, not individual classes
- Driven by quality attributes β performance, scalability, security, maintainability
A useful definition: "Architecture is the decisions you wish you could get right early, because they're hard to change later."
Why Architecture Matters
Poor architectural decisions compound over time. A system with no clear layering becomes a "big ball of mud" β everything depends on everything else, no part can be changed safely, and no one understands the whole. The cost of adding new features grows while confidence in the stability of existing ones falls.
Good architecture:
- Makes the system easier to reason about
- Allows teams to work on different parts independently
- Localises the impact of change
- Enables testing without the full system
Architectural Layers
The most fundamental architectural pattern in enterprise applications is layering β organising code into horizontal tiers where each layer has a defined responsibility and depends only on the layers below it.
The Classic Three-Layer Architecture
βββββββββββββββββββββββββββββββββββββββ
β Presentation Layer β JSF views, REST controllers
βββββββββββββββββββββββββββββββββββββββ€
β Business Logic Layer β Services, domain rules, workflows
βββββββββββββββββββββββββββββββββββββββ€
β Data Access Layer β Repositories, JPA entities, queries
βββββββββββββββββββββββββββββββββββββββ
β
βΌ
Database
Presentation Layer Handles user interaction and display. In a web application this is the JSF XHTML views and their backing beans. In a REST API it is the JAX-RS resource classes. This layer should contain no business logic β only coordination between the UI and the service layer.
Business Logic Layer Contains the domain rules, workflows, and calculations that make the application valuable. This is where decisions are made: whether a patient is eligible for a service, how a bill is calculated, whether a stock issue is approved.
Data Access Layer Handles persistence β reading and writing to the database. JPA entities, repositories, and JPQL queries live here. This layer should contain no business logic.
The Rule of Layering
Each layer may only depend on the layer directly below it. The presentation layer must not import from the data access layer. The data access layer must not call service methods.
Violations create tangled dependencies that are hard to test and dangerous to change.
MVC β Model-View-Controller
MVC is a pattern for organising the presentation layer itself. It separates:
- Model β the data and business state (JPA entities, service return values)
- View β the rendered output (XHTML pages, JSON responses)
- Controller β the coordination layer that handles input, invokes services, and prepares the view
User Input
β
βΌ
βββββββββββ invokes βββββββββββββββββββ
βControllerβ ββββββββββββββΆ β Service Layer β
βββββββββββ ββββββββββ¬βββββββββ
β β
β updates β reads/writes
βΌ βΌ
ββββββββββ ββββββββββββ
β View ββββ renders ββββββββ Model β
ββββββββββ ββββββββββββ
In Java EE with JSF:
- The View is the XHTML page
- The Controller is the CDI backing bean (
@ViewScoped,@SessionScoped) - The Model is the JPA entity returned by the service
Hexagonal Architecture (Ports and Adapters)
A more sophisticated pattern that places business logic at the centre, surrounded by adapters for the outside world.
ββββββββββββββββββββββββββββββββββββ
β β
DB ββββΆβ ββββββββββββββββββββββββββββββ βββββ REST API
β β β β
UI ββββΆβ β Business Logic β βββββ Messaging
β β β β
LIMS ββββΆβ ββββββββββββββββββββββββββββββ βββββ Batch Jobs
β β
ββββββββββββββββββββββββββββββββββββ
Ports (interfaces)
Adapters (implementations)
Port: An interface defined by the business logic describing what it needs from the outside world (StockRepository, NotificationPort).
Adapter: A concrete implementation of a port that connects the business logic to a real technology (JpaStockRepository, EmailNotificationAdapter).
Key benefit: The business logic can be tested completely without any infrastructure β substitute test adapters for real ones.
Microservices vs. Monolith
Monolith
A single deployable unit containing all application functionality.
Advantages:
- Simple to develop, test, and deploy
- No network overhead between modules
- Transactions span the whole system easily
- Suitable for small-to-medium systems and small teams
Disadvantages:
- Scaling requires scaling everything, not just the bottleneck
- A fault anywhere can affect everything
- Large teams have difficulty working independently
- Technology choices are uniform β you cannot use a different language for one module
Microservices
Separate, independently deployable services that each own a specific business capability and communicate over a network.
Advantages:
- Services scale independently
- Teams can deploy independently
- Technology choice per service
- Failure is isolated
Disadvantages:
- Significant operational complexity (service discovery, load balancing, distributed tracing)
- Network calls are slower and less reliable than in-process calls
- Distributed transactions are hard
- Requires mature DevOps practices
Mini-Services
A pragmatic middle ground: a modular monolith or a small number of clearly bounded services. Modules have clean interfaces between them but share a deployment and database. This allows iterative extraction into true microservices later without premature complexity.
Event-Driven Architecture
Components communicate by emitting and consuming events rather than calling each other directly.
Stock Issue Service βββΆ [STOCK_ISSUED event] βββΆ Billing Service
βββΆ Inventory Reporter
βββΆ Reorder Monitor
Strengths:
- Very loose coupling β the emitter does not know who consumes the event
- New consumers can be added without touching the emitter
- Natural audit trail
Weaknesses:
- Harder to trace request flow
- Error handling is more complex
- Eventual consistency must be managed
In Java EE, CDI Events provide a lightweight in-process event mechanism without requiring a message broker.
Architectural Quality Attributes
Good architecture is explicitly designed for quality attributes (also called non-functional requirements):
| Attribute | Question it answers |
|---|---|
| Performance | How fast does the system respond under load? |
| Scalability | Can capacity be added as load increases? |
| Availability | What is the acceptable downtime? |
| Security | How does the system protect data and prevent unauthorised access? |
| Maintainability | How easy is it to change the system? |
| Testability | How easy is it to verify the system works correctly? |
| Deployability | How easy and safe is it to release changes? |
| Interoperability | How easily does the system integrate with others? |
Architectural decisions are trade-offs between these attributes. A decision that improves performance may reduce maintainability. Naming the trade-offs explicitly is part of good architectural practice.
Common Anti-Patterns
| Anti-pattern | Description |
|---|---|
| Big Ball of Mud | No structure; everything depends on everything |
| God Service | One service that does everything |
| Chatty Interface | Hundreds of fine-grained calls where a few coarse ones would do |
| Shared Database | Multiple services writing directly to each other's tables |
| Distributed Monolith | Microservices that are so tightly coupled they must always deploy together |
| Anemic Domain Model | Entities that are pure data with all logic in service classes |
Previous: SE-06: Clean Code
Next: SE-08: Refactoring