~7m24:09
ExtremeCode

Стать архитектором ПО за 24 минуты (за 12 на x2)

Oct 29, 2026

Read: ~8m · You save: 16 min

Becoming a Software Architect in 24 Minutes (12 at 2x)

Learn how real software architects model business logic with DDD: entities, value objects, aggregates, and policies, through a driver salary example, plus an AI practic

Most application code can be organized with an established set of approaches. This article covers two of them: layered architecture as a way to separate responsibilities, and Domain-Driven Design (DDD) as a method for structuring business logic. The examples use a driver payroll process.

Organizing Application Code

Any application larger than a few lines needs an organizing principle. The standard approach is to divide code by area of responsibility. For example, code that interacts with a database belongs in a separate group, and code that renders the user interface belongs in another.

Sorting code into groups is not enough. The groups also need rules for how they interact. Without such rules, the groups gradually merge, and a change to a user interface button requires editing database queries. A common rule forbids user interface code from accessing the database directly, and forbids database code from controlling the user interface.

These groups are called layers, and the overall structure is called layered architecture. Real implementations differ from textbook diagrams. Layers may be split into modules, interaction rules may vary, and some architectures are drawn as onions or hexagons. Architects disagree on the overall shape. They largely agree on the domain model, the part of the application that represents business rules. Approaches to building it are well established and do not depend on the chosen overall shape.

A complex domain model is not required for every application. When one is needed, DDD provides the established approaches.

What Business Logic Is

Businesses constantly make decisions: which goods to ship, which carrier to assign, and how much to pay for delivery. Some steps still depend on people. Software can handle the decisions that follow fixed rules. The rules by which a program makes such decisions form the business logic.

Example: Driver Payroll

Assume a company applies the following rules to driver Uncle Vasya's pay:

  • 1,000 rubles for each completed trip
  • 20 rubles for each kilometer driven
  • a 25% surcharge for night trips

A simple implementation works as follows:

  1. Retrieve the list of Uncle Vasya's trips.
  2. Check which trips are completed.
  3. Add 1,000 rubles for each completed trip.
  4. Add the mileage payment.
  5. For night trips, add 25% on top.

For a small company with one or two drivers and stable rules, this implementation is sufficient.

The rules change over time. Accounting may request the following:

  • Taxes that change as often as 20 times a week must be included.
  • Salaries may be paid only for confirmed trips.
  • Incorrect payments must be prevented.
  • Drivers may have different rates.
  • New drivers must receive 25% more.
  • A trip must not be paid twice.
  • A driver may enter the same trip twice in the ledger.

Each new condition added to a single method increases its complexity. Developers could add more conditions and checks, or build an internal system to manage them. DDD offers a tested set of approaches for organizing this logic.

Core Principle of DDD

In DDD, business logic is not scattered across classes. The business is represented as objects, and each object has its own responsibility. Trips, drivers, rates, and payroll ledgers exist in the business independently of the software.

Each object has rules. These rules are called invariants. An invariant is a constraint that must hold whenever an object's state changes.

Entity

Consider a trip. Without rules, a trip is a set of data: distance, a night-trip flag, and a completion status. Any code can change any field. A trip can be marked as completed, or its distance can be changed from 10 km to 1,000 km.

The business, however, has restrictions:

  • After a trip is confirmed, its distance cannot change.
  • A trip can be confirmed only after it is completed.

In DDD, the trip itself enforces these invariants. Instead of allowing direct changes to fields, the trip provides specific actions, such as completing, confirming, or setting the distance. Each action checks the invariants before it runs. If an action violates an invariant, the trip rejects it.

This object is an entity. An entity has its own identity. Each trip has a unique identifier. Its distance and state can change, but it remains the same trip. An entity both stores data and enforces the rules related to its state.

Access control is part of this design. If fields are public, any code can bypass the checks. Restricting access modifiers keeps state changes within the entity's rules.

Aggregate and Aggregate Root

Now consider a payroll sheet. It contains charges. Each charge includes a trip identifier and a payout amount.

Suppose two charges reference the same trip. Each charge is valid on its own. The trip exists, and the amount is calculated correctly. However, together they result in a double payment. A charge cannot detect this problem because it does not know about other charges. The payroll sheet has the complete list and can check for duplicates.

The sheet also has its own status. Once it is approved, its charges cannot change, because the payment has been made and the data has been submitted to the tax authority.

To enforce these rules, external code must not modify charges directly. All changes pass through the payroll sheet, which checks the constraints before it performs an action.

The sheet and its charges form an aggregate. The payroll sheet, through which all changes pass, is the aggregate root.

Definition: An aggregate is a group of related objects that are managed as a single unit through a root entity, while maintaining common invariants.

If an entity contains other objects and controls their changes and compliance with shared invariants, it is likely an aggregate.

Value Object

The driver rate is applied to all drivers. It does not have an individual identity. Two rate objects with identical values are interchangeable, because only their values matter. Such objects are called value objects.

A trip, by contrast, has its own identity. Suppose Uncle Vasya drives the same route twice, with the same distance, pay, and time of day. These are still two different trips. Swapping them could result in one trip being paid twice and the other not being paid at all.

Value objects are usually immutable. To change a rate, the program creates a new rate object instead of modifying the existing one. This prevents other parts of the program from changing a rate during a calculation.

Other value objects include money, distances, ranges, and dates. For example, 1,000 rubles equals another 1,000 rubles. A unique identifier is not needed to establish this equality.

Policy

The question remains: which object should calculate a driver's accrual?

  • The trip entity should not calculate pay. A trip describes transportation: distance, time, and completion status. Pay rules belong to a different area.
  • The payroll aggregate should not perform the calculation either. It is responsible for accruals and their consistency. Adding tariffs, night bonuses, and other pay rules would mix separate responsibilities.

A policy isolates a specific business rule or a group of related rules in a separate object. In this case, a Trip Payment policy calculates the payment for a trip. It does not store trip or payroll state. It receives a confirmed trip and a driver's rate, then calculates the amount using the defined rules.

If the formula changes or new bonuses are introduced, the change is made in the policy. The trip entity and the payroll aggregate remain unchanged. A business that uses fundamentally different payment methods can have several policies.

Putting the Model Together

The original payroll method handled all tasks in one place: it checked trip status, calculated payments, and summed the results. It worked for simple rules. As requirements grew, responsibilities were distributed across the domain model.

The payroll process now runs as follows:

  1. Retrieve the confirmed trips for Uncle Vasya for the required period.
  2. For each trip, determine the rate that was active when the trip was completed.
  3. Pass the trip and the rate to the Trip Payment policy, which calculates the accrual.
  4. Ask the payroll sheet to add the amount.
  5. The payroll sheet checks whether the entry is allowed, whether it already exists, and whether changes are prohibited.
  6. Send the sheet to accounting for approval.
  7. After approval, the aggregate prevents changes to the accruals.

Application Layer

Code that coordinates this process belongs to the application layer. It determines which actions run and in what order. The domain model determines the rules under which those actions are allowed.

If the application layer contains an error, the domain model rejects the invalid action. Because each entity enforces its own invariants, developers no longer need to remember every constraint in every part of the application.

Discovering the Model with the Business

Identifying entities, aggregates, value objects, and policies requires dialogue with a domain expert. The expert describes the business. Developers identify the concepts and rules within that description.

Typical questions include the following:

  • Trips: What is a trip? How is one trip distinguished from another? Can its state change? Which changes are prohibited after completion? The answers reveal a trip's identity, lifecycle, and state restrictions. This indicates an entity.
  • Rates: If two drivers have identical pay for a trip, mileage, and night work, is there any difference between their rates for the calculation? If the values alone matter, the rate is a value object.
  • Payment: How is payment calculated? Which bonuses, exceptions, or special conditions apply? Can different methods exist? If a group of rules determines the payment for a trip and does not belong to the trip or the sheet, it is a policy.
  • Constraints: Which changes must not happen separately? The accountant explained that charges are collected into a sheet, a trip cannot appear twice in the same sheet, and an approved sheet cannot change. These are common invariants controlled at the level of the whole sheet. This indicates an aggregate.

The business does not need to use DDD terms. The developer identifies these concepts from the way the business operates.

Ubiquitous Language

The model must use the same terms as the business. If accounting distinguishes between a completed trip and a confirmed trip, the model must also treat them as separate concepts. In DDD, this shared vocabulary is called the ubiquitous language. Developers and business representatives use the same words for the same concepts.

Practicing with an AI Stakeholder

The most difficult part of DDD is not writing an entity or an aggregate. The difficult part is extracting correct requirements from a stakeholder who describes the business in long, unstructured form.

An AI model can simulate this stakeholder. The model can generate a fictional business with processes, rules, and constraints. The practitioner interviews this virtual representative, identifies requirements, and builds a domain model with entities, value objects, aggregates, and policies. After the practitioner completes the model, the AI model can audit it and identify cases where the implementation violates the business rules.

The AI model is useful for practicing questions, not for generating the design. A simulated stakeholder responds more consistently than a real one, so the prompt can add realistic imprecision to the dialogue.

Introduction: What Architects Actually Do

Architecture is not about class naming or inheritance. Code organization is considered settled, while the hard problems lie in infrastructure and in modeling the business.

  • The speaker says most people wrongly think architects mainly decide which class to inherit from or what namespace to use.
  • Code organization is described as dull, simple, and well-established, with a well-defined meta.
  • The speaker claims architecture conference speakers on code organization are often functional programmers or fringe figures.
  • The video promises to teach a way to turn AI into a trainer for practicing architectural skills.

Layered Architecture

Code is split into areas of responsibility, called layers, with rules restricting how they interact. Architectures can vary, but the domain model is where approaches agree.

  • Code interacting with the database and code rendering the UI are separated into their own layers.
  • Rules are needed to prevent layers from merging into one mega-dumpster; for example, UI code may not access the database directly, and database code may not control the UI.
  • This structure is called layered architecture, a name the speaker jokingly contrasts with dumpster architecture.
  • In practice, layers can be split into modules, interaction rules vary, and some use onion or hexagonal depictions.
  • Architects broadly agree on the domain model, regardless of which architectural shape they prefer.

Domain Model and Domain Logic

The domain model is the part of an application where architects have established, vetted approaches. DDD is the commonly known set of these approaches.

  • A complex domain model is not needed in every application, but when it is, inventing a proprietary revolutionary architecture usually fails.
  • Domain logic consists of business decisions, such as which goods to send, who delivers them, and how much to pay.
  • Domain logic is the set of rules by which a program makes such decisions.

Business Logic Example: Driver Salaries

A driver salary calculation example shows how simple code grows into a tangle as business rules change, motivating DDD.

  • Initial rules: 1,000 rubles per completed trip, plus 20 rubles per kilometer driven, plus a 25% surcharge for night trips.
  • The speaker later refers to the per-kilometer rate as 25 rubles, which is inconsistent with the initial 20 rubles.
  • A simple implementation is sufficient for a micro-company with one or two drivers and stable rules.
  • Changing requirements include variable taxes, paying only for confirmed trips, correcting past payment errors, different driver rates, 25% higher pay for newbies, and preventing duplicate payments for the same trip.
  • Adding more conditions to one method risks turning the code into unmaintainable code, so established approaches are preferred.

Entities and Invariants

An entity is an object with its own identity that protects its own invariants, meaning rules that must hold whenever its state changes. Changes go through explicit actions, not direct field access.

  • The speaker calls the rules that must hold during state changes constraints, or invariants in DDD terminology.
  • Example: after a flight is confirmed, its distance cannot be changed, and a flight can be confirmed only after it is completed.
  • Relying on every developer to check invariants leads to errors when one check is forgotten.
  • In DDD, the entity itself enforces its invariants through actions such as completing, confirming, or setting the distance.
  • An entity has a unique identifier, so it remains the same flight even when its distance and state change.
  • Fields should not be public, and access modifiers should be carefully managed to prevent bypassing invariants.

Aggregates and the Aggregate Root

An aggregate is a cluster of related objects managed as one unit through a root entity, which enforces general invariants across the group. The payroll sheet with its charges is the example.

  • The payroll sheet holds a list of charges, each with a trip ID and a payout amount.
  • Each charge is valid on its own, but two charges for the same trip cause double payment, which the individual charge cannot detect.
  • The payroll sheet checks whether a trip was already added and enforces its own status; once approved, charges cannot be modified.
  • External code must not modify charges directly; changes go through the payroll sheet.
  • The sheet with its charges is the aggregate, and the payroll sheet through which changes pass is the aggregate root.
  • Definition: an aggregate is a group of associated objects managed as a single unit through a root entity, while maintaining general invariants.
  • If an entity manages the changes and invariants of other objects, it is likely an aggregate root.

Value Objects

A value object is defined by its values rather than an identity, and it is usually immutable. Tariffs and money are typical examples.

  • A tariff applied to multiple drivers is a value object, because two tariffs with the same values are interchangeable.
  • Two trips with the same distance, pay, and time are still different trips, so trips have identity and are entities.
  • Value objects are usually immutable; changing a rate creates a new tariff object instead of modifying the existing one.
  • Immutability prevents another part of the program from changing a tariff in the middle of salary calculations.
  • Other value objects include money, distances, ranges, and dates; 1,000 rubles equals another 1,000 rubles.

Policies

A policy isolates a specific business rule or group of rules, such as payment calculation, into a separate object that does not store state about the trip or the sheet.

  • Putting payment calculation into the trip entity is considered silly, because trips describe transportation, not wages.
  • Putting payment calculation into the payroll sheet mixes accrual consistency with tariffs, bonuses, and other rules.
  • A Trip Payment policy receives a confirmed trip and a driver's rate and calculates the amount due.
  • Policies make it clear where to change formulas or bonuses without touching the trip entity or payroll aggregate.
  • Several payment policies can coexist if the business uses fundamentally different payment methods.

Assembling the Payroll Process

The payroll process combines entities, value objects, aggregates, and policies, while an application layer orchestrates the sequence of actions and the domain layer enforces the rules.

  • The payroll process gets confirmed trips for a period, determines the active rate at completion, passes both to the Trip Payment policy, and adds the result to the payroll sheet.
  • The payroll sheet checks whether the entry is allowed, whether it already exists, and whether changes are prohibited.
  • Once the sheet is approved for accounting, the aggregate no longer allows changes to accruals.
  • The application layer decides which actions are performed and in what sequence.
  • The domain layer decides under which rules actions are allowed; errors in the application layer are caught by domain invariants.

Discovering the Domain with Experts

Domain modeling starts with questions to business experts, not with class design. The concepts they use should be reflected directly in the code.

  • Questions for identifying an entity include what a trip is, how trips are distinguished, and whether a trip can change state.
  • Questions for identifying a value object include whether two tariffs with the same values differ in any way for calculation.
  • Questions for identifying a policy include how payment is calculated, what bonuses or exceptions exist, and whether different methods exist.
  • Questions for identifying an aggregate include which constraints must hold when related data changes and what must not be done separately.
  • The accountant distinguishes completed trips from confirmed trips, so the model must keep these as different concepts.
  • Ubiquitous Language means developers and business representatives use the same terms for the same things.

Practicing with an AI Stakeholder

A neural network can act as a virtual business stakeholder to practice domain modeling. The user interviews it, builds the model, and then asks the AI to audit the architecture for rule violations.

  • The AI generates a fictional business with processes, rules, and constraints.
  • The user identifies entities, value objects, aggregates, and policies through questions to the virtual representative.
  • The AI audits the resulting architecture and points out where classes violate business rules.
  • The speaker says the text version of the prompt is on his Telegram channel, and suggests it may be posted in the comments.
  • Adding a persona with realistic inconsistencies, referred to as Kaban Kabanovich, makes the simulated stakeholder more human.
This analysis saves 14 min of your time (24:09 → ~10m)