Sitemap

Briefing Document: Distributed Systems and Software Architecture

6 min readMar 26, 2025

--

This document provides a briefing on the main themes and important ideas presented in the provided sources, which cover various aspects of software architecture, focusing on distributed systems, scalability, caching, security, and event-driven architectures.

Main Themes

  • Evolution of Software Architecture: The sources illustrate a progression in architectural considerations, starting with fundamental concepts like layered architecture and non-functional requirements (maintainability, scalability, performance, reliability), moving towards the complexities of distributed systems and optimization techniques like caching.
  • Challenges of Distributed Systems: A central theme is the inherent complexity introduced by distributed systems. This includes communication challenges (network reliability, security, latency), scalability and resiliency challenges (handling large traffic and data, fault tolerance), and coordination challenges (data consistency, distributed transactions, failure detection).
  • Importance of Non-Functional Requirements: The sources consistently emphasize the crucial role of non-functional requirements in driving architectural decisions. Factors like scalability, performance, maintainability, reliability, and security heavily influence the choice of architectural patterns and implementation strategies.
  • Optimization Techniques: Techniques like caching and horizontal scaling are presented as key strategies for improving the performance and scalability of software systems, particularly in distributed environments.
  • Event-Driven Architecture (EDA) and Microservices: The concepts of EDA, event sourcing, CQRS, and the Saga pattern are introduced as approaches to address the challenges of building decoupled and scalable microservices.
  • Security in Microservices: The unique security considerations in a microservices architecture are highlighted, emphasizing the need for distributed authentication and authorization mechanisms, as well as the importance of defining security goals and trust boundaries.
  • Service Discovery: The necessity and various approaches to service discovery in dynamic distributed environments are explained.

Most Important Ideas and Facts

1. Fundamentals of Software Architecture:

  • Software architecture involves planning, designing, and constructing systems to meet both functional (what the system should do) and non-functional requirements (how the system should behave — “-ilities”).
  • As Ralph Johnson stated, software architecture is about “the important stuff… whatever that is.”
  • Common non-functional requirements include maintainability, scalability, reliability, usability, and efficiency.
  • Architectural decisions are also influenced by constraints like legal compliance, costs, and time to market.
  • It’s crucial to prioritize requirements and make trade-offs, as conflicts can arise. For example, strict time to market might necessitate dropping features.
  • Start designing with the most important aspects first to avoid over-engineering (YAGNI — You Ain’t Gonna Need It).
  • Common architectural patterns include Layered, Event Driven, Microkernel, Microservices, and Space-based.

2. Scaling Software Systems:

  • Vertical Scaling (Scale Up): Making a single node stronger by adding more hardware. Suitable for moderate increases in load.
  • Horizontal Scaling (Scale Out): Splitting the load across multiple servers. Preferred for handling millions of users and improving reliability through redundancy.
  • Horizontal scaling introduces complexities related to state management and the CAP theorem (Consistency, Availability, Partition Tolerance).
  • Strategies for horizontal scaling include:
  • Load Balancing: Distributing traffic across multiple identical servers.
  • Sharding/Partitioning: Splitting data across different servers, allowing independent scaling. Data can be split vertically by feature or horizontally by value or hash.
  • Replication: Creating multiple copies of data on different servers, beneficial for read-intensive applications and improving availability.
  • Scaling can be expensive and requires careful consideration of trade-offs.

3. Caching for Performance:

  • Cache stores data so future requests for the same data can be served faster.
  • Different types of cache include CDN (for static assets), in-memory/local cache (on the service), and distributed cache (e.g., Redis, Memcached).
  • “There are only two hard things in computer science: cache invalidation and naming things.” — Phil Karlton.
  • Cache invalidation is complex and needs careful configuration to avoid serving outdated data or excessive invalidation causing cache misses.
  • Cache can complicate the system, especially with multiple levels of caching.
  • Caching is often an optimization that can be added at a later stage.

4. Distributed Systems: Concepts and Challenges:

  • A Distributed System is “a group of nodes located on different networked computers, which communicate and coordinate their actions by passing messages to one another, in order to achieve a common goal.”
  • Driven by the need for high availability, reliability, scalability, and performance for always-on applications serving billions of users.
  • Key characteristics and challenges are:
  • Communication Challenges: Unreliable networks (lost packets, failures, congestion, latency), security concerns, need for network protocols, service discovery, and idempotency.
  • Scalability and Resiliency Challenges: Handling increased load, maintaining availability during failures (timeouts, retries, replication, sharding), and ensuring data consistency.
  • Coordination Challenges: Maintaining data consistency across multiple nodes (leader-follower patterns), handling distributed transactions, time synchronization, and failure detection.
  • Maintainability: Managing and updating a complex network of services.

5. Microservices Architecture:

  • Microservices is “a way to design software as a suite of independently deployable services. Usually around a business capability.”
  • Contrast with monolithic architectures where all modules are within a single system and often share a common database.
  • In microservices, each service can have its own database, leading to challenges in coordinating transactions across services.

6. The Saga Pattern for Distributed Transactions:

  • Provides a way to manage transactions across multiple microservices in a transaction-like manner (without strict ACID properties).
  • Involves a sequence of local transactions in different services, with compensating actions to revert changes if a transaction fails.
  • Two main implementation approaches:
  • Orchestration: A central orchestrator service manages the flow and calls other services.
  • Choreography: Services communicate via events, and each service reacts to relevant events.

7. Event-Driven Architecture (EDA):

  • Based on the concept of “Events” (“something that happens”) that are communicated between services.
  • Contrast with “Commands” (“an order” expecting a response).
  • Services produce and consume events, leading to decoupled architectures and inverted dependencies.
  • Events are typically immutable and can be processed in parallel by multiple services.
  • Example: An “OrderCreated” event can be consumed by the Inventory service, the Payment service, and a Recommendation service.

8. Event Sourcing and CQRS:

  • Event Sourcing: Instead of storing the current state, the system stores a sequence of events that represent state changes in an “Event Log”. The current state can be derived by replaying the events.
  • Enables audit logs, temporal queries (going back in time), and easy replication of data.
  • Snapshots can be used to optimize by storing the current state at a point in time, reducing the need to replay the entire event log.
  • Command Query Responsibility Segregation (CQRS): Separates the write (Command) and read (Query) operations for a data store.
  • Allows for independent scaling of read and write services.
  • Can optimize data models for different read and write requirements.
  • Often used with Event Sourcing, where write operations generate events and read services maintain their own data views based on these events.

9. Microservices Security Architecture:

  • Securing microservices differs from securing monoliths due to the distributed nature and multiple service instances.
  • Essential security concepts:
  • Authentication: Verifying the identity of the user (the “principal”). Often handled at the API Gateway using mechanisms like passwords.
  • Authorization: Determining what actions an authenticated user is allowed to perform. Typically managed using tokens (e.g., JWT, PASETO) that carry permissions.
  • Session Management: Defining the context and duration for which a user’s access is valid. In microservices, sessions are often stateless and managed through tokens.
  • Access Control: Enforcing authorization policies at different layers of the application.
  • Audit Logging: Keeping an unmodifiable record of user actions for security analysis and compliance.
  • Rate Limiting: Controlling the amount of resources a user can consume to prevent denial-of-service attacks.
  • Security goals (e.g., Confidentiality, Integrity, Availability — the CIA triad) are non-functional requirements for security.
  • Understanding trust boundaries (where the level of trust changes) is crucial for security design.
  • Threat modeling (e.g., using the STRIDE model) helps identify potential threats and vulnerabilities (e.g., those listed by OWASP).
  • Security mechanisms (encryption, authentication, access control, audit logging, rate limiting, input validation) are used to mitigate threats.
  • Security should be risk-based, as 100% security is often impractical or cost-prohibitive.

10. Service Discovery:

  • The process of locating resources (applications/services) on a network, especially important in dynamic microservices environments where service instances can change frequently.
  • Requires a Service Registry, a database storing services and their locations (e.g., IP addresses).
  • Service Registry needs to be kept up-to-date with new, removed, and unhealthy services.
  • Two main categories based on where discovery happens:
  • Client-Side Service Discovery: The client directly queries the Service Registry and then makes a request to a chosen service instance. Requires client-side implementation and introduces coupling.
  • Server-Side Service Discovery: The client makes a request to a load balancer or a dedicated discovery service, which then forwards the request to an appropriate service instance. More language-agnostic but requires infrastructure setup.
  • Two main ways to maintain the Service Registry:
  • Self Registration: Each service is responsible for registering and unregistering itself with the registry.
  • Third-party Registration: An external application (Registrar), like a Sidecar or a Service Orchestration Engine (e.g., Kubernetes), observes service status and updates the registry automatically.

These excerpts provide a valuable overview of key concepts in modern software architecture, particularly in the context of building scalable and resilient distributed systems using microservices. The emphasis on non-functional requirements, the challenges introduced by distribution, and the various patterns and techniques to address these challenges are central themes.

--

--