← Back to Software Architecture

5 Software Architecture Patterns

Software architecture patterns for layering, service boundaries, and data ownership.

Software ArchitectureDesign PatternsSoftware Architecture

Architectural patterns are useful when they clarify where responsibilities live and how change should flow through the system. They are not badges of maturity. A pattern is good only if it makes the dominant failure modes, deployment shape, and team boundaries easier to manage.

1. Layered architecture

Layered architecture groups code by responsibility, often into presentation, application, domain, and data access layers. A request enters at the top, business rules are applied in the middle, and persistence happens below.

This pattern remains common because it is easy to teach and works well for many internal systems. A single deployable service with clear layers can stay maintainable for a long time if the domain is moderate and the team is small.

The weakness appears when layers become permission to create god services and passive data models. If every change leaks through controllers, services, repositories, and mappers whether it needs to or not, the architecture adds ceremony without real separation.

2. Microservices

Microservices split a system into independently deployable services, each owning a bounded area of functionality and usually its own data store. This helps when different parts of the system need different scaling behaviour, release cadence, or operational isolation.

The real benefit is not small codebases by itself. It is the ability to let one domain evolve without redeploying everything else. A billing service and a search service often have different traffic patterns, storage needs, and reliability requirements.

The tradeoff is distributed systems overhead. Network calls replace local method calls, observability gets harder, and data consistency becomes an explicit design problem. A premature move to microservices can create more coordination work than product value.

3. Event-driven architecture

Event-driven architecture connects components through events rather than direct synchronous calls. A service publishes that something happened, such as order_created, and other services react asynchronously.

This pattern is strong when one action should trigger several downstream behaviours without forcing the producer to know about each consumer. It improves decoupling and smooths spikes because work can be buffered in queues or logs.

The cost is indirectness. Tracing a user action across publishers, brokers, consumers, retries, and dead-letter queues is harder than following a single request-response chain. Teams also need to plan for duplicate delivery, out-of-order events, and replay semantics.

4. Hexagonal architecture

Hexagonal architecture, also called ports and adapters, keeps domain logic at the centre and treats databases, message brokers, HTTP frameworks, and other infrastructure as replaceable adapters around it.

This is valuable when the core business rules need to remain stable while integrations change. It encourages testing domain behaviour without booting the whole stack and makes boundaries between policy and mechanism more explicit.

The danger is abstraction that arrives before pressure. If the domain is trivial, building ports for every dependency can feel like architecture theatre. Hexagonal structure pays off when the business rules are important enough to deserve strong protection from framework churn.

5. Microkernel or plugin architecture

A microkernel architecture keeps a small stable core and lets optional capabilities arrive as plugins or modules. IDEs, e-commerce platforms, and internal developer portals often use this model because the core workflow is stable while integrations vary by customer or team.

Its strength is controlled extensibility. New features can be added without turning the core into a tangle of special cases. It also helps when different deployments need different feature sets.

The main challenge is designing a plugin contract that is strong enough to be safe but flexible enough to stay useful. Versioning, isolation, and startup order become real operational concerns once third-party or semi-independent modules are involved.

The important question is not which pattern is most fashionable. It is which one matches the real source of complexity. Layered architecture is often enough for a coherent application. Microservices help when operational and organisational boundaries truly diverge. Event-driven systems help when work must fan out asynchronously. Hexagonal architecture protects valuable business rules from infrastructure churn. Microkernel designs help when extensibility is the main requirement. A sound architecture makes the hard parts visible instead of hiding them behind a diagram.