Software Architecture: Essence, Styles, Patterns, Principles, History

Lecture



Software architecture is the foundation on which any digital solution is built. It defines not individual lines of code or specific technologies, but the overall way the system is organised: how its parts are connected, how responsibilities are distributed, and how the project's growth, resilience and clarity are ensured.

Without architecture, a software product may work in its early stages, but over time it becomes increasingly difficult to maintain. Changes start to take more time, bugs appear more often, and the system's evolution becomes unpredictable. That is precisely why architecture is needed not only by large projects, but by any software solution that has to evolve, scale and remain manageable.

It is important to distinguish between architectural styles and patterns.

  • An architectural style is a broader category that sets out the general principles for building systems (for example, client-server, microservices, layered architecture).

  • An architectural pattern is a specific solution for organising the structure of components within a system and the interaction between them.

The essence of software architecture

The essence of architecture lies in creating a clear structure for the system. It helps define which parts the program consists of, what role each part plays, and how they interact with one another.

Architecture answers not so much the question «how to write the code» as the question «how to organise the system so that it can live and evolve». It is a kind of framework that keeps the project from descending into chaos and helps technical decisions to be made deliberately.

Good architecture makes a system easier for developers to understand, more resilient to change and more convenient to maintain. It reduces the dependency of individual parts on one another and makes it possible to evolve the product without rewriting it entirely.

Why software architecture is needed

Architecture is needed so that a software system is not merely functional, but manageable, reliable and ready for change. It helps you think through in advance the key decisions that will affect the entire life cycle of the product.

The main goals of architecture:

Goal Value
Organising the system Helps split the project into comprehensible parts
Managing complexity Prevents the code base from growing chaotically
Supporting growth Makes it easier to add new capabilities
Improving reliability Reduces the risk of errors when making changes
Easier maintenance Makes the system clearer for the team
Aligning decisions Helps everyone on the project think along the same lines

Architecture matters especially when a project runs for a long time, a team works on it, requirements change, and the product has to remain stable and easy to extend.

Core principles of architecture

Software architecture rests on a number of general principles. They help create systems that are easier to understand, change and maintain.

Principle What it means
Separation of concerns Each part of the system should perform its own clearly defined job
Loose coupling Components should not depend excessively on one another
High cohesion within a module Everything relating to one task should be located together logically
Simplicity The architecture should solve the problem without unnecessary complication
Flexibility The system should allow changes without destroying the whole structure
Scalability of thinking The architecture should let the project grow without losing manageability
Clarity Decisions should be clear not only to their author, but to other team members as well
Resilience to change Changing one part should not unpredictably break the entire system

The main idea behind these principles is to make the system not a random collection of code, but a well-considered construction in which every decision has its own place and purpose.

Requirements for good architecture

Good architecture should take into account not only current tasks, but also the possible evolution of the product. It does not have to be complex, but it must be thought through well enough to withstand change.

The key requirements include:

Requirement Description
Clarity The structure of the system should be easy to explain
Maintainability The code should be convenient to change and support
Extensibility New features should be added without breaking existing logic
Reliability The system should remain stable under changes and load
Flexibility The architecture should adapt to new requirements
Controlled complexity The project must not turn into a set of random dependencies
Consistency of solutions Coherent approaches should be used throughout the system
Testability The system should be convenient to verify for correctness

It is important to understand that good architecture is not the most complex architecture. On the contrary, strong architecture is often distinguished by clarity, restraint and a good fit with the project's real tasks.

What architecture means for the team

Architecture matters not only for the system itself, but also for the people who work with it. It creates a common language between developers, analysts, managers and other project participants.

When the architecture is clear, the team makes decisions faster, finds the right parts of the system more easily and argues less about the basic organisation of the project. This is especially important as the team grows, when the project is handed over to new developers, or during long-term maintenance of the product.

Architecture helps avoid a situation in which every developer implements solutions their own way. It sets common rules and makes the product's evolution more predictable.

Architecture as a tool for growth

A software system almost always changes: new requirements appear, business processes shift, the number of users grows, new features are added. Architecture allows the system to adapt to these changes.

Without an architectural approach, every change can become more expensive and riskier. With well-considered architecture, growth remains more controllable: the team understands where to add new logic, which parts a change will affect and how not to break mechanisms that already work.

Architecture, then, is not a one-off decision at the start of a project, but an ongoing tool for managing the quality and evolution of a software product.

Levels of architecture

Level What it describes Examples
UI level How the interface and its logic are arranged MVC, MVP, MVVM
Application architecture How the code inside the application is organised Layered, Clean, Hexagonal, Onion, Modular Monolith
Domain level How business logic is modelled DDD, Aggregates, Bounded Contexts
System level How the application is divided into large parts Monolith, Client-Server, SOA, Microservices
Integration level How the parts of the system communicate REST, GraphQL, Message Queue, Event-Driven
Deployment / Infrastructure level How the system is deployed and scaled Serverless, Cloud-Native, Kubernetes-based architecture

Software Architecture: Essence, Styles, Patterns, Principles, History

Types of software architecture

Below is an overview of popular types of software architecture. The years are approximate, because many approaches emerged gradually: first as a practice, then as a term.

Architecture Approximate years of appearance / popularisation Level

Main characteristic

advantages and disadvantages

Monolithic architecture 1960s–1970s Application architecture

The entire application is built and deployed as a single whole

+Simple development at the start; easy to run; easier to debug; less infrastructure

-Hard to scale in parts; over time the code can become tangled; any change requires deploying the whole application

Modular monolith 1990s / 2000s Application architecture

A single deployable artifact, but with a clear internal split into modules

+A good balance of simplicity and structure; easier to maintain; simpler than microservices

-Requires discipline; module boundaries are easy to violate; scaling is still confined to a single application

Client-server architecture 1980s System architecture

The client requests data or functions from the server

+Separation of the UI and server-side logic; centralised data; convenient for networked applications

-The server can become a bottleneck; dependence on the network; synchronisation complexity

Three-tier architecture 1990s Application / system architecture

A split into presentation, business logic and data layer

+A clear structure; layers can be developed separately; well suited to enterprise systems

-Can become too formal; business logic is sometimes smeared across the layers

N-tier architecture 1990s System architecture

Several logical or physical tiers: UI, API, business logic, database and so on

+Flexibility; the load can be distributed; convenient for enterprise systems

-More complexity; more network calls; higher latency

Layered Architecture 1970s / 1980s Application architecture

The code is divided into layers: controller, service, repository, database

+Simple and clear; well suited to most CRUD applications; easy to teach a team

-Can lead to an anaemic model; a lot of «glue» classes often appear; does not always express the business domain well

MVC 1979 / 1980s UI / application architecture

A split into Model, View, Controller

+Convenient for separating the interface from the logic; widely known; many frameworks

-In large projects the controller can grow out of hand; different frameworks interpret MVC differently

MVP 1990s UI architecture

The View is as passive as possible, while the Presenter drives the display logic

+Convenient for testing UI logic; a good separation of responsibilities

-A lot of boilerplate code; the Presenter can become too large

SOA, Service-Oriented Architecture 1990s / 2000s Enterprise / system architecture

The system consists of services, often with centralised integration

+Reuse of services; integration of large enterprise systems; standardisation

-Can be heavyweight; complex infrastructure; a centralised bus often becomes a bottleneck

Enterprise service bus (ESB) 2000s integration architecture

regarded as an implementation of Service-Oriented Architecture (SOA).

The core principle of a service bus is to concentrate message exchange between different systems at a single point where, when required, transactional control, data transformation and message persistence are provided. All settings for processing and transmitting messages are likewise assumed to be concentrated at that single point and are expressed in terms of services, so that when any information system connected to the bus is replaced, there is no need to reconfigure the remaining systems.

The name was chosen by analogy with a computer's system bus, which allows several devices to be connected and data to be transferred between them over a single set of conductors.

Key benefits

  • Scales from point solutions to enterprise-wide deployment (a distributed bus).
  • More attention is paid to configuration than to programming the integration.
  • There is no centralised rules engine and no centralised broker.
  • Easy to connect and disconnect, with a loosely coupled design.

Key disadvantages

  • Lower communication speed, especially for already compatible services.
  • A single point of failure can bring down all communications across the enterprise.
  • High configuration and maintenance complexity.
Microservice architecture 2010s System / deployment architecture

The system consists of small, independently deployable services

+Independent deployment; scaling of individual parts; autonomous teams; different technologies for different services

-High infrastructure complexity; distributed transactions; monitoring, networking, DevOps and observability become mandatory

Event-Driven Architecture 2000s / 2010s System architecture

Components communicate through events

+Loose coupling; good scalability; convenient for asynchronous processes

-Harder to debug; eventual consistency; harder to trace the flow of execution

Event Sourcing* 2000s / 2010s Architectural pattern for storing state

State is stored as a sequence of events

+A complete history of changes; state can be restored to any point in time; convenient for auditing

-Complexity of event migrations; harder queries; requires a good event model

DDD Architecture* 2003 Domain model architecture / design approach

The architecture is built around the business domain, bounded contexts and aggregates

+Well suited to complex business logic; helps define the boundaries of modules and services; a shared language with the business

-Hard to master; unnecessary for simple CRUD; requires the involvement of domain experts

MVVM around 2005 UI architecture

The View is linked to the ViewModel through binding

+Well suited to reactive UIs; less manual code for updating the interface; convenient to test the ViewModel

-Complexity of binding mechanisms; sometimes hard to debug; the ViewModel can grow out of hand

Hexagonal Architecture / Ports and Adapters 2005 Application architecture

The domain is isolated from the outside world through ports and adapters

+Good testability; independence from the database, UI and frameworks; clean business logic

-More abstractions; can be excessive for simple CRUD systems

ECS (Entity-Component-System

around 2007

1998 — Thief: The Dark Project: the first ideas of composing game objects out of components.

2002 — Dungeon Siege (Scott Bilas): formalisation of the «Data-Driven Game Object System» approach.

2007 — Operation Flashpoint: Dragon Rising: the development team experimented with ECS.

architectural pattern

makes it possible to separate data from logic, providing flexibility, scalability and high performance.

The main elements of ECS
Entity — an entity, an abstract object (for example, a player, an enemy, a projectile). Usually it is simply an identifier.

Component — data describing the properties of an entity (position, health, speed). Components contain no logic.

System — a set of functions that process component data (for example, the movement system updates position based on speed).

Clean Architecture around 2012 Application architecture

Dependencies point inwards: towards the domain/use cases

+Independence from frameworks; high testability; protects business logic well

-Many layers and interfaces; requires experience; can be excessive for small projects

Onion Architecture around 2008 Application architecture

The domain at the centre, infrastructure on the outside

+Similar to Clean/Hexagonal; separates business logic well from storage and delivery details

-Can be difficult to apply correctly; requires discipline with dependencies

CQRS 2010s Architectural pattern

Separation of commands that change data from queries that read it

+Reads and writes can be optimised separately; convenient for complex domains; combines well with event sourcing

-Complicates the system; duplication of models; eventual consistency

Serverless Architecture 2014–2015 Cloud / deployment architecture

Code runs as functions or managed services without server management

+No need to manage servers; auto-scaling; pay per use

-Vendor lock-in; cold start; harder local debugging; platform limitations

Microkernel / Plugin Architecture 1980s / 1990s Application architecture

There is a system core and pluggable plugins

+Flexible extension; convenient for IDEs, CMSs, browsers and platforms

-Complexity of the plugin API; risk of incompatibility; the core must be very well designed

Pipe and Filter 1970s Data processing architecture

Data passes through a chain of independent filters

+Easy to compose; good for ETL, compilers and stream processing

-Not always suitable for interactive systems; state can be hard to manage

Peer-to-Peer, P2P 1990s / 2000s Distributed architecture

Nodes interact directly without a central server

+No single point of failure; good distribution; scales with the number of participants

-Security complexity; harder to control data; instability of nodes

REST Architecture 2000 API architecture

Resources are accessed through HTTP methods and URIs

+Simplicity; well suited to web APIs; caching; broad support

-Not always convenient for complex operations; can lead to overfetching/underfetching

GraphQL Architecture 2015 API architecture

The client itself specifies which data it needs

+Less redundant data; convenient for complex frontend applications; a single endpoint

-Caching complexity; risk of heavy queries; protection against overload is required

Broker / Message Queue Architecture 1990s / 2000s Integration architecture

Components communicate through a message broker

+Asynchrony; resilience to temporary failures; loose coupling

-Monitoring complexity; latency; duplicate messages are possible

Cloud-Native Architecture 2010s Cloud / system architecture

Applications are designed for containers, automatic scaling and fault tolerance

+High scalability; convenient deployment; resilience; well suited to Kubernetes

-A high barrier to entry; complex infrastructure; requires mature DevOps

The most important thing

Not all of these architectures are alternatives to one another. They are often combined.

For example, a real system might look like this:

Microservices
  ├── Order Service: Clean Architecture + DDD + REST
  ├── Payment Service: Hexagonal Architecture + Events
  ├── Notification Service: Event-Driven
  └── Frontend: MVVM or MVC

In other words, microservices are responsible for dividing the system up, DDD for the business model, Clean/Hexagonal for the structure of the code inside a service, and REST/Event-driven for the interaction between the parts.

Monolithic software architecture

Monolithic software architecture is an approach in which all the main components of an application are combined into a single software unit.

In a monolith, the user interface, business logic, database access and other modules are usually developed, deployed and scaled together. Such an application is easier to create and launch in the early stages, but as the system grows it becomes harder to change, test, scale and maintain.

Software Architecture: Essence, Styles, Patterns, Principles, History

A modular monolith is an architectural approach in which the application remains a single deployable software unit but is internally divided into independent logical modules.

Each module is responsible for its own area of functionality — users, orders, payments or reports, for example. Unlike an ordinary monolith, a clear internal structure matters here: modules have their own boundaries, depend on one another as little as possible and interact through defined interfaces.

This approach preserves the simplicity of developing and deploying a monolith, but makes the system clearer, more maintainable and easier to split into microservices later if needed.

Software Architecture: Essence, Styles, Patterns, Principles, History

Client-server architecture is an approach in which the system is divided into two main parts: the client and the server.

The client is responsible for interacting with the user: it displays the interface, accepts the user's actions and sends requests. The server processes those requests, executes business logic, works with the database and returns the result to the client.

This approach makes it possible to store data and logic centrally on the server, while the client applications can vary: a website, a mobile app, a desktop application or another device.

Software Architecture: Essence, Styles, Patterns, Principles, History

Three-tier architecture is an approach to building an application in which the system is divided into three logical tiers:

1. The presentation tier is responsible for the user interface: web pages, a mobile app or a desktop client.

2. The business logic tier handles user requests and executes the application's rules, validations, calculations and core logic.

3. The data tier is responsible for storing and retrieving data, usually through a database or file storage.

The main idea of this architecture is to separate the interface, the logic and the data so that the system is easier to develop, maintain, test and scale.

Software Architecture: Essence, Styles, Patterns, Principles, History

N-tier architecture is an architectural approach in which the application is divided into several independent logical levels — tiers.

Such a system usually includes a presentation tier, a business logic tier, a data access tier, a data storage tier and additional tiers such as an API gateway, cache, integrations or security services.

The main idea of N-tier architecture is to divide responsibility between the layers. Each tier performs its own task and interacts with adjacent tiers through defined interfaces. This makes the system more flexible, scalable and maintainable than a simple monolith or a classic three-tier architecture.

Software Architecture: Essence, Styles, Patterns, Principles, History

Layered Architecture is an architectural approach in which the application is divided into several layers, with each layer responsible for its own part of the system.

Typically a presentation layer, an application layer, a business logic layer, a data access layer and a data storage layer are identified. Upper layers use lower ones, but must not interfere directly with their internal implementation.

The main idea of layered architecture is the separation of responsibilities. This helps organise the code and simplifies maintenance, testing and evolution of the system, since changes in one layer affect the other parts of the application less.

Software Architecture: Essence, Styles, Patterns, Principles, History

MVC architecture is an architectural approach in which the application is divided into three main parts: Model, View and Controller.

The Model is responsible for the data, the business logic and the rules for working with it. The View is responsible for displaying information to the user. The Controller accepts the user's actions, processes requests and connects the model with the view.

The main idea of MVC is to separate the application logic, the data and the user interface. This simplifies development, testing and maintenance of the system, because changes to the interface affect the business logic less, and vice versa.

Software Architecture: Essence, Styles, Patterns, Principles, History

MVP architecture is an architectural approach in which the application is divided into three parts: Model, View and Presenter.

The Model is responsible for the data, the business logic and working with data sources. The View is responsible only for displaying the interface and passing on the user's actions. The Presenter receives events from the View, calls the Model, processes the result and updates the View.

The main idea of MVP is to separate the user interface from the control logic. This simplifies testing, maintenance and changes to the interface, because the core interaction logic lives in the Presenter while the View remains as «passive» as possible.

Software Architecture: Essence, Styles, Patterns, Principles, History

MVVM architecture is an architectural approach in which the application is divided into three main parts: Model, View and ViewModel.

The Model is responsible for the data, the business logic and working with data sources. The View is responsible for the user interface and the display of data. The ViewModel connects the View and the Model: it prepares data for display, handles user commands and manages the state of the interface.

The main idea of MVVM is to separate the interface from the display logic and state. This is convenient for applications with an active user interface, since the View can update automatically when the data changes through data binding (data binding).

Software Architecture: Essence, Styles, Patterns, Principles, History

SOA / Service-Oriented Architecture is an approach in which an application is built as a set of separate services that interact with one another through standardised interfaces.

Each service is responsible for a particular business function, such as managing users, orders, payments or notifications. Services can be built with different technologies and reused across different systems.

The main idea of SOA is to divide a large system into autonomous services with clear interaction contracts. This increases flexibility and component reuse and simplifies integration between different applications and enterprise systems.

Software Architecture: Essence, Styles, Patterns, Principles, History

Microservice architecture is an approach in which an application is built as a set of small independent services.

Each microservice is responsible for a separate business function, such as users, orders, payments, notifications or the product catalogue. Such services usually have their own logic, their own API and often a separate database.

The main idea of microservice architecture is to divide a large system into autonomous parts that can be developed, deployed, scaled and updated independently of one another. This increases the system's flexibility, but it also complicates the management of interaction, monitoring, security and infrastructure.

Software Architecture: Essence, Styles, Patterns, Principles, History

Event-Driven Architecture is an approach in which the components of a system interact through events.

An event means that something has happened in the system: a user has registered, an order has been created, a payment has gone through, an item has been added to the warehouse. One component publishes an event, and other components subscribe to it and react independently.

The main idea of Event-Driven Architecture is to reduce tight coupling between the parts of a system. Components do not have to call one another directly: they exchange events through a message broker or an event bus. This increases flexibility, scalability and the convenience of asynchronous processing, but it requires good control over delivery, event ordering and error handling.

Software Architecture: Essence, Styles, Patterns, Principles, History

Event Sourcing: an architectural pattern for storing state

Event Sourcing is an architectural pattern in which the state of the system is stored not as a current record, but as the sequence of events that brought the system to that state.

For example, instead of storing only the order status «paid», the system saves a chain of events: order created → item added → payment completed → order confirmed. The current state can be restored by applying all the events in sequence.

The main idea of Event Sourcing is to record the history of changes as the primary source of truth. This gives a full audit trail of actions, the ability to restore the state at any point in time and easier analysis of business processes, but it requires careful handling of event versions, processing order and the storage of large volumes of history

Software Architecture: Essence, Styles, Patterns, Principles, History

DDD Architecture

DDD Architecture / Domain-Driven Design is an approach to software design in which the structure of the system is built around the problem domain and the business logic rather than around the database or the framework.

The main idea of DDD is to make the code as close as possible to real business concepts: order, customer, payment, product, warehouse, reservation and so on. Business rules are placed in the domain layer, while technical details — the database, API, UI, message queues — are moved outwards.

DDD helps build a system around the business model, where the main rules live in the domain layer and the technical details are plugged in separately and do not drive the application's architecture.

The main kinds of classes in DDD

1. Entity

An object with a unique identifier.

Example:

User
Order
Product
Payment

For instance, two orders may have the same total, but they are different entities because they have different OrderId values.

public class Order
{
    public OrderId Id { get; private set; }
    public OrderStatus Status { get; private set; }
    public List<OrderItem> Items { get; private set; }

    public void Confirm()
    {
        if (!Items.Any())
            throw new Exception("Cannot confirm an empty order");

        Status = OrderStatus.Confirmed;
    }
}

2. Value Object

An object without an identifier of its own. Its significance is determined by the values of its fields.

Example:

Money
Email
Address
FullName
DateRange
public class Money
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
}
3. Aggregate

A group of related objects that change as a single whole.

Example:

Order
 ├── OrderItem
 ├── DeliveryAddress
 └── PaymentInfo

An aggregate usually has a main object — the Aggregate Root.

Order = Aggregate Root
OrderItem = an internal part of the aggregate

External code should work only through Order, rather than modifying OrderItem directly.

4. Repository

A class or interface for saving and retrieving aggregates.

public interface IOrderRepository
{
    Task<Order?> GetByIdAsync(OrderId id);
    Task SaveAsync(Order order);
}

The repository hides database details from the domain logic.

5. Domain Service

A class for business logic that does not belong to any one specific entity.

Example:

PricingService
PaymentPolicy
DeliveryCostCalculator
public class DeliveryCostCalculator
{
    public Money Calculate(Address address, List<OrderItem> items)
    {
        // business rules for calculating delivery
    }
}

6. Application Service

Coordinates the execution of use cases: create an order, pay for an order, cancel an order.

public class CreateOrderUseCase
{
    private readonly IOrderRepository _orders;

    public async Task Execute(CreateOrderCommand command)
    {
        var order = Order.Create(command.UserId, command.Items);
        await _orders.SaveAsync(order);
    }
}

It should not contain complex business logic. It calls domain objects and repositories.

7. Domain Event

A fact that has already occurred in the problem domain.

OrderCreated
OrderPaid
UserRegistered
ProductReserved
public class OrderPaidEvent
{
    public OrderId OrderId { get; }
    public DateTime PaidAt { get; }
}

A typical folder structure for a DDD project

An example for an online store project:

src/
 └── Shop/
     ├── Domain/
     │   ├── Orders/
     │   │   ├── Order.cs
     │   │   ├── OrderId.cs
     │   │   ├── OrderItem.cs
     │   │   ├── OrderStatus.cs
     │   │   ├── IOrderRepository.cs
     │   │   ├── OrderCreatedEvent.cs
     │   │   └── Services/
     │   │       └── OrderPricingService.cs
     │   │
     │   ├── Users/
     │   │   ├── User.cs
     │   │   ├── UserId.cs
     │   │   └── Email.cs
     │   │
     │   ├── Products/
     │   │   ├── Product.cs
     │   │   ├── ProductId.cs
     │   │   └── Money.cs
     │   │
     │   └── Shared/
     │       ├── Entity.cs
     │       ├── ValueObject.cs
     │       └── DomainEvent.cs
     │
     ├── Application/
     │   ├── Orders/
     │   │   ├── CreateOrder/
     │   │   │   ├── CreateOrderCommand.cs
     │   │   │   └── CreateOrderHandler.cs
     │   │   ├── PayOrder/
     │   │   │   ├── PayOrderCommand.cs
     │   │   │   └── PayOrderHandler.cs
     │   │   └── CancelOrder/
     │   │       ├── CancelOrderCommand.cs
     │   │       └── CancelOrderHandler.cs
     │   │
     │   └── Users/
     │       └── RegisterUser/
     │           ├── RegisterUserCommand.cs
     │           └── RegisterUserHandler.cs
     │
     ├── Infrastructure/
     │   ├── Persistence/
     │   │   ├── AppDbContext.cs
     │   │   ├── OrderRepository.cs
     │   │   └── UserRepository.cs
     │   │
     │   ├── Messaging/
     │   │   └── EventPublisher.cs
     │   │
     │   └── ExternalServices/
     │       ├── PaymentGateway.cs
     │       └── EmailSender.cs
     │
     └── Presentation/
         ├── Controllers/
         │   ├── OrdersController.cs
         │   └── UsersController.cs
         │
         └── DTOs/
             ├── CreateOrderRequest.cs
             └── RegisterUserRequest.cs

The layers in brief

Presentation

Accepts HTTP requests, renders the UI, calls Application.

Application

Describes the use cases: create an order, pay for an order, register a user.

Domain

Contains the main business logic: entities, aggregates, rules, events, value objects.

Infrastructure

Works with the database, external APIs, message queues, file systems and other technical details.

Software Architecture: Essence, Styles, Patterns, Principles, History

Hexagonal Architecture / Ports and Adapters is an architectural approach in which the application's business logic sits at the centre of the system and does not depend on external technologies.

At the centre is the application core: the domain model, the business rules and the use cases. External elements — the database, the web interface, APIs, message queues, file systems or third-party services — are connected to the core through ports and adapters.

Ports describe the interaction interfaces, while adapters implement the connection to specific technologies. The main idea of this architecture is to isolate the business logic from the infrastructure so that the system is easier to test, change and port to other technologies.

Software Architecture: Essence, Styles, Patterns, Principles, History

ECS / Entity-Component-System is an architectural approach often used in game engines and simulations, where the objects of the system are split into entities, components and systems.

Entity is an object or identifier, such as a player, an enemy, an item or a vehicle. Component stores an entity's data, such as position, health, speed or appearance. System contains the logic that processes components, such as movement, physics, rendering or collisions.

The main idea of ECS is to separate data from behaviour. Entities consist of a set of components, and systems process every entity that has the required components. This makes the architecture flexible, performant and convenient for bulk processing of large numbers of objects.

Why ECS is convenient in games

  • Performance: data is stored compactly and makes efficient use of the processor cache. This is especially important for games with large numbers of objects.

  • Flexibility: new components and systems are easy to add without rewriting code.

  • Scalability: suitable for complex game worlds with thousands of entities.

  • Reuse: systems can be applied to different entities, which reduces code duplication.

ECS compared with OOP

Approach Data storage Logic Performance Applicability
OOP Inside objects Methods inside classes Degrades with large numbers of objects Suitable for smaller projects
ECS In separate structures (components) In systems High, thanks to Data-Oriented Design Ideal for large-scale games

In Unity the situation is as follows:

  • The base level (GameObject + Component) — out of the box Unity uses a component model: any object in a scene is a GameObject to which various MonoBehaviour components can be attached. This is not pure ECS, but an approach close to it in spirit. In other words, base Unity is already «component-based», but not «system-based» in the strict ECS sense.

  • Pure ECS (Unity DOTS) — a full implementation of ECS arrived as a separate package: DOTS (Data-Oriented Technology Stack). It includes:

    • Entities — the ECS core, where entities are IDs and data is stored in struct components.

    • Jobs System — multithreaded processing of systems.

    • Burst Compiler — a compiler for maximum code optimisation.

  • Conclusion:

    • Built-in Unity (GameObject + MonoBehaviour) is a component architecture, but not a full-fledged ECS.

    • For genuine ECS you need the separate DOTS package (Entities + Jobs + Burst).

Approach Built-in Unity Unity DOTS (ECS)
Model GameObject + MonoBehaviour Entity + Component + System
Performance Average, depends on the number of objects High, optimised for cache use and multithreading
Flexibility Convenient for smaller projects Ideal for large worlds and thousands of objects
Package required? No, built in Yes, Entities/Jobs/Burst are needed

Software Architecture: Essence, Styles, Patterns, Principles, History

Clean Architecture is an architectural approach in which the application's business logic sits at the centre of the system and does not depend on external details.

Inside are the entities, the business rules and the use cases. The outer layers are responsible for the interface, controllers, databases, APIs, frameworks and other technical details.

The main idea of Clean Architecture is that dependencies must point inwards, towards the business logic. This makes it possible to change the database, the UI, the framework or external services without significantly affecting the core of the application, and it also simplifies testing and maintenance of the system.

Software Architecture: Essence, Styles, Patterns, Principles, History

Onion Architecture

Onion Architecture is an architectural approach in which the application is built as a series of concentric layers around the domain model.

At the centre is the Domain: entities, value objects, business rules and domain services. The outer layers contain the Application / Use Cases, interfaces, infrastructure, the database, APIs, the UI and external services.

The main idea of Onion Architecture is that dependencies point inwards only, towards the domain logic. The domain must not depend on the database, on frameworks or on the interface. This makes the business logic easier to test, maintain and port to other technologies.

Software Architecture: Essence, Styles, Patterns, Principles, History

CQRS / Command Query Responsibility Segregation is an architectural approach in which operations that change data and operations that read data are separated.

CQRS usually distinguishes two sides:

The Command Side is responsible for actions that change the state of the system: create an order, pay for an order, cancel a reservation, edit a user profile.

The Query Side is responsible only for reading data: retrieve a list of orders, show a user card, produce a report or statistics.

The main idea of CQRS is not to mix write and read logic. This makes it possible to optimise writes and reads separately, use different data models, improve performance and simplify complex business logic. CQRS is often used together with Event Sourcing and Event-Driven Architecture.

Software Architecture: Essence, Styles, Patterns, Principles, History

Serverless Architecture is an architectural approach in which the developer does not manage servers directly but hosts code and services on a cloud platform.

The application usually consists of individual functions, API gateways, managed databases, message queues, file storage and other cloud services. Code is triggered by an event: an HTTP request, a file upload, a message in a queue, a data change or a schedule.

The main idea of Serverless is to focus on business logic and hand infrastructure, scaling, fault tolerance and server startup over to the cloud provider. This is convenient for event-driven and variable workloads, but it requires taking into account platform limitations, cold starts, the cost of invocations and dependence on the cloud vendor.

Software Architecture: Essence, Styles, Patterns, Principles, History

Microkernel / Plugin Architecture is an architectural approach in which the application consists of a small base core and a set of pluggable modules — plugins.

The system core contains the minimal essential functionality: starting the application, common rules, life-cycle management, and the registration and invocation of plugins. Plugins add specific capabilities: data import, reports, integrations, additional formats, business extensions or user-defined functions.

The main idea of Microkernel Architecture is to make the system extensible without modifying the core. New features can be added, removed or updated as separate plugins, which is convenient for platforms, IDEs, CMSs, browsers, enterprise systems and products with a large number of extensions.

Software Architecture: Essence, Styles, Patterns, Principles, History

Pipe and Filter Architecture is an architectural approach in which data processing is broken down into a sequence of independent stages.

Filter is an individual handler that receives input data, performs one specific operation on it and passes the result on.
Pipe is the mechanism for passing data between filters.

The main idea of Pipe and Filter is to build the system as a processing chain: input data → filter 1 → filter 2 → filter 3 → result. This approach is convenient for stream processing, ETL processes, compilation, logging, image, audio and video processing, and message queues. It makes it easier to add, replace and reuse individual processing stages.

The way Middleware works in Laravel, for example, can be regarded as an instance of Pipe and Filter Architecture, but not as the whole of Laravel's architecture.

In Laravel an HTTP request passes through a chain of middleware:

HTTP Request
   ↓
Middleware 1: authorisation check
   ↓
Middleware 2: CSRF check
   ↓
Middleware 3: logging
   ↓
Middleware 4: localisation
   ↓
Controller
   ↓
HTTP Response

Each middleware resembles a Filter:

Filter = Middleware

It receives the request, performs a single task and passes the request on.

And passing the request between middleware resembles a Pipe:

Pipe = the chain that passes Request → next()

But an important point: Laravel as a whole is not solely a Pipe and Filter architecture. Laravel more often combines several approaches:

MVC — Controllers, Models, Views
Layered Architecture — Route → Controller → Service → Repository → DB
REST - support for resource routes and controllers
Pipe and Filter — Middleware pipeline
Event-Driven — Events / Listeners / Queues

Software Architecture: Essence, Styles, Patterns, Principles, History

Peer-to-Peer Architecture / P2P is an architectural approach in which the participants in a system interact directly with one another, without a mandatory central server.

Every node in the network, or peer, can be both a client and a server at the same time: it can request data from other nodes and itself provide data or resources to other participants.

The main idea of P2P is to distribute the storage, processing and exchange of data across a multitude of equal nodes. This increases the fault tolerance and scalability of the system, but it complicates node discovery, data synchronisation, security, access control and network management.

Software Architecture: Essence, Styles, Patterns, Principles, History

REST Architecture / Representational State Transfer is an architectural style for building networked APIs, most often web services over HTTP.

In REST the system works with resources: users, orders, products, payments. Each resource has its own URL, and operations on it are performed with standard HTTP methods: GET to read, POST to create, PUT/PATCH to modify, DELETE to remove.

The main idea of REST is to make the interaction between client and server simple, uniform and independent of the internal implementation. The client sends a request to a resource, the server processes it and returns a representation of the data, usually in JSON format. REST is well suited to web applications, mobile applications and integrations between systems.

Software Architecture: Essence, Styles, Patterns, Principles, History

GraphQL Architecture is an architectural approach to building APIs in which the client itself specifies which data it needs and the server returns exactly the requested structure.

Instead of numerous REST endpoints such as /users, /orders, /products, a single GraphQL endpoint is normally used, for example /graphql. The client sends a request describing the fields it needs, and the server processes it through the schema, queries, mutations and resolvers.

The main idea of GraphQL is to give the client a flexible way of retrieving related data with no superfluous or missing fields. This is convenient for web and mobile applications where different screens require different sets of data, but it requires control over query complexity, authorisation, caching and resolver performance.

Software Architecture: Essence, Styles, Patterns, Principles, History

Broker / Message Queue Architecture is an architectural approach in which the components of a system exchange data not directly but through an intermediary service — a message broker.

One service sends a message to a queue or topic, and another service receives and processes it. For example, the order service publishes an OrderCreated message, and the notification service, the warehouse and analytics receive it via the broker.

The main idea of this architecture is to reduce direct dependency between services and make data exchange asynchronous. This increases the scalability and resilience of the system: if the recipient is temporarily unavailable, the message can stay in the queue and be processed later. At the same time it is important to control redelivery, message ordering, processing errors and idempotency.

Software Architecture: Essence, Styles, Patterns, Principles, History

Cloud-Native Architecture is an approach to building applications designed from the outset to run in a cloud environment.

Such systems are usually built from microservices, containers, managed cloud services, APIs, message queues, automatic scaling, monitoring and CI/CD processes.

The main idea of Cloud-Native is to make the application flexible, scalable and resilient to failures. Components can be developed, deployed and scaled independently, while the infrastructure is managed automatically using cloud platforms, container orchestration and DevOps practices.

Software Architecture: Essence, Styles, Patterns, Principles, History

Architecture description languages

Architecture description languages (ADLs) are used to describe software architecture. Various organisations have developed a number of different ADLs, including AADL (an SAE standard), Wright and Acme (developed at Carnegie Mellon University), xADL (developed at UCI), Darwin (developed at Imperial College London), DAOP-ADL (developed at the University of Málaga) and ByADL (University of L’Aquila, Italy). The concepts of component, connector and configuration are common to all these languages. Besides these specialised languages, the Unified Modeling Language (UML) is also often used to describe architecture.

Conclusion

Software architecture is the foundation that determines a system's resilience, clarity and capacity to evolve. It helps manage complexity, distribute responsibility between the parts of the system, reduce the risks of change and create the conditions for long-term maintenance of the product.

Its main value lies not in formally following rules, but in creating a clear and dependable structure. Good architecture makes a software product more flexible, the team more effective and the evolution of the system more predictable.

This is precisely why architecture is one of the key components of quality software development. It helps you not merely to create a working system, but to build a solution that can grow, change and retain its value over time.

See also

  • [[b12453]]
  • [[b14264]]
  • [[b8566]]
  • [[b14266]]
  • ArchiMate
  • Architecture description language
  • Architectural framework
  • Architectural pattern (computer science)
  • Architectural style
  • Anti-pattern
  • Attribute-driven design
  • C4 model
  • Computer architecture
  • Distributed Data Management Architecture
  • Distributed Relational Database Architecture
  • Systems architecture
  • Systems design
  • Software Architecture Analysis Method
  • List of software architecture styles and patterns
  • Software architecture description
  • Time-triggered system
  • View model

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Software and information systems development"

Terms: Software and information systems development