https://github.com/mhadidg/software-architecture-books
https://odrotbohm.de/2023/07/sliced-onion-architecture/ https://www.happycoders.eu/software-craftsmanship/hexagonal-architecture/
as opposed to a simple layered architecture
There are several software architecture patterns that are used to structure software systems. These architectures offer different ways to handle complexity, scalability, testability, and maintainability, depending on the needs of the system. Here’s an overview of major software architecture patterns, explaining their concepts, characteristics, pros, cons, and typical use cases.
- Description: A traditional architecture where the application is divided into layers, each layer having a specific role (e.g., Presentation, Business Logic, Data Access).
- Flow: The flow is top-down—the user interface (UI) interacts with the business logic, which in turn interacts with the data access layer.
- Presentation Layer: Handles the user interface and user requests.
- Application/Business Logic Layer: Contains business rules, logic, and operations.
- Domain Layer (sometimes merged with the Business Logic Layer): Represents core business objects (e.g., entities, domain rules).
- Data Access Layer: Responsible for interacting with the database or any external data sources.
- Simplicity: Easy to understand and implement.
- Separation of concerns: Each layer has a specific responsibility.
- Modularity: Easier to maintain individual layers.
- Tight coupling: Layers are often tightly coupled, which reduces flexibility.
- Limited flexibility: Hard to isolate a specific layer for testing or reuse without dependencies.
- Cross-cutting concerns: Hard to handle concerns like logging, security, and caching across layers.
- Enterprise applications: Often used in legacy or traditional business systems, CRUD (Create, Read, Update, Delete) applications.
- Description: An architecture where communication between components happens through events. Components or services are loosely coupled and communicate asynchronously.
- Flow: Producers emit events, and consumers (subscribers) react to those events.
- Event Producers: Emit events when something changes in the system.
- Event Consumers: Listen for specific events and react to them (e.g., update a database, trigger workflows).
- Event Brokers: Middleware that distributes events to relevant consumers (e.g., message queues like Kafka, RabbitMQ).
- Scalability: Asynchronous and loosely coupled components can scale independently.
- Flexibility: Easy to add new components by simply subscribing to events.
- Resilience: System components can fail without affecting the whole system if handled properly (retry mechanisms).
- Complex debugging: Harder to trace and debug event flows due to the asynchronous nature.
- Consistency: Eventual consistency can cause temporary data inconsistencies.
- Overhead: Requires proper management of event queues, brokers, and processing times.
- Distributed systems, microservices, IoT (Internet of Things), and applications requiring high scalability or real-time processing.
- Description: A collection of small, independent services that communicate via APIs. Each service represents a specific business capability and can be developed, deployed, and scaled independently.
- Flow: Services communicate with each other either synchronously (e.g., REST APIs) or asynchronously (e.g., message queues).
- Independent Services: Each service has its own logic and database (optional).
- Service Communication: Typically through lightweight protocols (HTTP, messaging queues).
- API Gateway (optional): A gateway that routes requests from clients to the correct microservices.
- Independence: Each service can be developed and deployed independently.
- Scalability: Services can scale individually, optimizing resource use.
- Resilience: If one service fails, others can continue to operate (with proper fault tolerance).
- Complexity: Distributed system complexity—managing services, communication, and consistency across services.
- Deployment overhead: Requires DevOps practices for continuous deployment, monitoring, etc.
- Data management: Transactions across services can be difficult to manage.
- Large-scale applications that need to be highly scalable and fault-tolerant.
- Companies like Netflix, Amazon, and Uber use microservices.
- Description: An older architectural style similar to microservices but typically more coarse-grained and less focused on small, independently deployable units. Services are reusable components that can interact across different platforms and technologies.
- Flow: Services are often exposed through an Enterprise Service Bus (ESB) that handles communication, orchestration, and security.
- Services: Self-contained units that provide a specific business function.
- Enterprise Service Bus (ESB): A middleware layer that handles service communication, routing, and orchestration.
- Service Registry: A registry where services can be discovered by other services.
- Loose coupling: Services are independent, promoting reusability and flexibility.
- Integration: Can integrate with legacy systems, mainframes, etc.
- Scalability: Individual services can be scaled as needed.
- Overhead: Complex to set up and manage the ESB, which can become a bottleneck.
- Latency: Communication through the ESB can introduce latency.
- Tightly coupled to ESB: Changes in the ESB can affect all services.
- Large enterprise systems, where integration with legacy applications is essential.
https://www.youtube.com/watch?v=YPmKHm7G19Q&list=PL6JIpdOjaDkkoM7pwxl6KW0iL9YtQa5Rw
- Description: An architecture that isolates the core business logic (domain) from external systems (e.g., databases, UI, APIs) using ports and adapters. Core logic does not depend on external systems.
- Flow: External systems interact with the application core via ports (interfaces). Adapters implement those ports and translate between the external systems and the core logic.
- Core Logic (Domain): Contains the business rules and logic.
- Ports: Interfaces that define how the core logic interacts with external systems.
- Adapters: Implement the ports and handle external communications (e.g., repositories, controllers).
- Testability: Core logic is isolated, making it easy to unit test.
- Decoupling: Business logic is independent of external systems, making it highly flexible.
- Resilience: Swapping out external systems (e.g., switching databases) can be done without impacting core logic.
- Complexity: Requires good design discipline and understanding of dependency inversion.
- Initial setup: More work upfront to define ports and adapters.
- Applications that need high flexibility and long-term maintainability, like microservices or enterprise-level applications.
- Description: A layered architecture where the core business logic is at the center, surrounded by other layers like application logic, frameworks, and external interfaces. Dependency inversion is a key principle, meaning that outer layers (UI, databases, frameworks) depend on the core, not vice versa.
- Flow: Outer layers interact with inner layers through interfaces.
- Entities: Core business rules.
- Use Cases: Application-specific business logic that uses the entities.
- Interface Adapters: Converts data from external sources (e.g., databases, APIs, UI) to formats understood by the use cases and entities.
- Frameworks & Drivers: External systems like databases, UI frameworks, etc.
- Testability: Core business logic is isolated and easy to test.
- Independence: Frameworks and databases can be swapped with minimal impact on the core logic.
- Flexibility: Changes in business logic do not affect external systems.
- Complexity: Requires significant initial setup and proper separation of layers.
- Over-engineering: Can be overkill for small or simple applications.
- Long-living systems that require high maintainability and testability (e.g., enterprise applications, large-scale systems).
- Description: A variant of Clean Architecture, but more focused on the domain model. The core domain logic is at the center, with dependencies pointing inwards. Outer layers (like UI, infrastructure) depend on the domain, not the other way around.
- Flow: External services interact with the core through adapters and interfaces, enforcing separation of concerns.
- Core: The domain model (entities and domain services).
- Service Layer: Application-specific logic, such as use cases.
- Infrastructure: Handles persistence, external APIs, UI, etc.
- Adapters: Provide access to external systems through well-defined interfaces.
- Domain-focused: Core domain logic is highly isolated and reusable.
- Testability: High testability due to separation of external systems from core.
- Flexible architecture: Makes it easier to replace external systems.
- Initial complexity: More complex and requires disciplined design.
- Overhead: Extra work to define and maintain interfaces and adapters.
- Applications with complex business rules and logic that require long-term flexibility and testability.
video by greg young is good
- Description: An architectural pattern where reading data (queries) and modifying data (commands) are handled by separate models. Often paired with Event Sourcing to capture state changes as events.
- Flow: The command model handles write operations, and the query model handles read operations, often using different databases for each.
- Command Model: Handles data modifications (create, update, delete).
- Query Model: Handles data reads, optimized for fast queries.
- Event Store (optional): Stores state changes as events, used in conjunction with the command model.
- Performance: Optimized for high read and write throughput.
- Flexibility: Different models can evolve separately for read and write operations.
- Scalability: Allows independent scaling of read and write operations.
- Complexity: Requires careful synchronization between the command and query models.
- Data consistency: Eventual consistency between the write and read models can be an issue.
- Applications with high scalability requirements and complex read-write workloads (e.g., large-scale e-commerce systems, banking applications).
Architecture | Key Characteristics | Best For |
---|---|---|
Layered (N-Tier) | Layers of responsibility, top-down flow | Traditional enterprise applications |
Event-Driven (EDA) | Asynchronous, loosely coupled, event-based | Distributed systems, IoT, real-time apps |
Microservices | Small, independent services, API-driven | Highly scalable, large-scale applications |
Service-Oriented (SOA) | Coarse-grained services, ESB for orchestration | Large enterprise systems |
Hexagonal | Core logic isolated from external systems | Applications with long-term flexibility |
Clean Architecture | Dependency inversion, core domain at center | Large, complex, maintainable systems |
Onion Architecture | Domain-driven, inward-facing dependencies | Domain-heavy, flexible applications |
CQRS | Separate read and write models | High-throughput systems, scalable apps |
The choice of architecture depends on factors such as scalability, testability, maintainability, and complexity. Traditional Layered Architecture is simple but rigid, whereas Microservices, Event-Driven, Hexagonal, and Clean Architecture offer more flexibility at the cost of added complexity. CQRS and Onion Architecture are especially useful when dealing with highly scalable or domain-centric systems.