Chapter 18: Reactive and onion architectures
Introduces reactive and onion architectures as two patterns the chapter will discuss, without explaining how either works.
After this chapter you will be able to
- Architectural scope and independence. Distinguish the scope of reactive architecture from onion architecture, and explain how the two patterns relate.
- Cause and effect coupling. Explain why adding a UI element that shows something about the cart can require changes in many places. Describe this as an n × m problem in which adding something to one column means modifying or duplicating things in the other.
- Event-driven response chains. Explain how reactive architecture extends ordinary event handlers: instead of one handler performing a sequence of steps, later steps respond to earlier ones.
- Reactive ordering and decoupling. Explain how reactive ordering differs from a typical sequence of instructions and why separating causes from effects can help or hurt readability.
- First-class shopping cart state. Explain why the shopping cart variable is wrapped in a ValueCell and describe how val() and update() replace direct reads and assignments.
- ValueCell watchers and changes. Explain that a ValueCell can have watcher functions that are called every time its state changes, allowing the cart to trigger an update to the shipping icons.
- Shipping icon watcher. Explain how adding update_shipping_icons() as a watcher of the shopping_cart ValueCell changes when the icons are updated and what handlers no longer need to do.
- FormulaCell derived values. Explain how a FormulaCell keeps a value derived from an upstream cell up to date and why its value is not updated directly.
- Mutable state and relative safety. Explain why functional software still needs mutable state and why cells holding immutable values are safer than regular global variables.
- ValueCell validity through updates. Describe how ValueCell initialization and update() can keep stored values valid, and distinguish that guarantee from ordering guarantees across timelines.
- Change handlers in reactive code. Describe the reconfiguration shown here: each part of the code becomes a handler for something else changing.
- Cause-independent cart updates. Explain why updating shipping icons in every cart-related button handler couples the update to particular causes, and how reactive architecture expresses the rule once for any change to the global cart.
- Cart cause-and-effect coupling. Explain why handling every cart change together with every resulting action creates growing maintenance work, and how separating causes from effects changes that growth.
- Pipeline composition and fit. Explain how a pipeline composes actions and calculations by passing one step’s output to the next, and when that pattern is or is not appropriate.
- Timeline flexibility without shared resources. Explain how reactive architecture splits work into shorter timelines and why those timelines are manageable in the shopping cart example.
- Choosing reactive architecture. Distinguish a situation suited to reactive architecture from one better expressed as a straightforward sequence of actions, using the two examples in the section.
- Reactive and onion architecture roles. Distinguish the reactive architecture’s focus on the order of actions from the onion architecture’s role in structuring an entire service that interacts with the outside world. Explain that reactive architecture may be nested inside onion architecture, but neither requires the other.
- Onion architecture layer roles. Describe the three general layer groupings and the kind of work each contains. Explain that these are typical groupings, not a required set of exact layers.
- Inward calls and interaction boundary. Explain the two rules for how layers interact: outside-world interaction stays in the interaction layer, and layers call inward without knowing about layers outside themselves.
- Actions calculations and data. Distinguish inert data, repeatable calculations, and actions whose behavior depends on the outside world or on when they run.
- Extraction and architectural shape. Explain why extracting calculations from actions can naturally produce something like the onion architecture, and state what the name is useful for here.
- Functions arranged in layers. Explain that stratified design arranges functions in layers according to which functions they call and which functions call them, helping clarify reuse, changeability, and testing priorities.
- Actions spreading up the graph. Trace the spreading rule in a stratified-design graph: when a box is an action, every box on its path to the top is also an action.
- Database foundation and actions. Explain why the section does not regard the traditional database-founded stack as functional, despite its possible use of calculations.
- Database placement and domain logic. Explain why a functional architecture moves the mutable database out of the layer stack and builds business and domain logic as calculations, with an action connecting those rules to database state.
- Onion architecture layer rules. Describe the onion architecture rules given here: world interaction belongs in the interaction layer, calls go inward, and layers do not know about layers outside themselves.
- Infrastructure change and domain reuse. Explain why onion architecture makes external services easier to change and domain calculations easier to reuse and test.
- Shopping cart layer responsibilities. Trace how the shopping-cart total example divides work between the interaction and domain layers, and contrast it with the typical architecture.
- Domain terms and layer placement. Explain how the terms used in a rule help determine whether it is a domain rule or belongs in the interaction layer. Apply the distinction to database fallback and web-request retries.
- Pragmatic architectural tradeoffs. Explain why making every domain operation a calculation is not always the clearest immediate choice, and describe how to handle compromises made for readability, development speed, or performance.
- Report database access boundary. Explain why fetching discounts inside the report's reduce callback would change the status of generateReport(), and where the database access should go instead.
- Reactive action sequencing. Explain that reactive architecture specifies which actions happen in response to another action, reorienting how actions are sequenced.
- Onion architecture perspective. Explain that onion architecture arises naturally from applying functional programming practices and offers a perspective that appears at every level of code.
What it builds on
- Summary of chaining tips (chapter 13) · Summarizes chaining functional tools into data-transformation steps.
- Uncertain ordering analysis (chapter 17) · Analyzes uncertain orderings of actions.
- Clarifying chains, method 1: Name the steps (chapter 13) · Composing calculations in chains of functional tools
- Timelines that share resources can cause problems (chapter 15) · The risk posed by timelines that share resources
- Actions, calculations, and data (chapter 3) · The distinction between actions and calculations.
- What is stratified design? (chapter 8) · The idea of organizing code into layers.
- Extracting a calculation from an action (chapter 4) · Shows how to extract a calculation from an action.
- Actions spread through code (chapter 3) · Explains that actions spread through code.
Section by section
Introduction
Introduces reactive and onion architectures as two patterns the chapter will discuss, without explaining how either works.
18.1 Two separate architectural patterns
The section distinguishes reactive and onion architectures by the level at which each applies and explains that they can be used independently.
- Architectural scope and independence: Distinguish the scope of reactive architecture from onion architecture, and explain how the two patterns relate.
Why it matters: The distinction helps you place each pattern at the right level without assuming you must adopt both.
Key terms: reactive architecture, onion architecture, sequences of actions, service
18.2 Coupling of causes and effects of changes
The section identifies an n × m coupling problem: adding a cart UI element requires changes across many other parts of the system.
- Cause and effect coupling: Explain why adding a UI element that shows something about the cart can require changes in many places. Describe this as an n × m problem in which adding something to one column means modifying or duplicating things in the other.
Why it matters: Recognizing the coupling makes clear why adding a small UI feature has become costly.
Key terms: n × m problem, user interface (UI), cart
18.3 What is reactive architecture?
Defines reactive architecture as organizing an application around responses to events, extending event handlers into chains of responses.
- Event-driven response chains: Explain how reactive architecture extends ordinary event handlers: instead of one handler performing a sequence of steps, later steps respond to earlier ones.
Why it matters: This distinction helps you recognize how reactive architecture organizes the same work differently from a step-by-step handler.
Key terms: reactive architecture, event handlers, events
18.4 Tradeoffs of the reactive architecture
This section explains how reactive architecture changes the expression of ordering and outlines the tradeoffs that change can bring.
- Reactive ordering and decoupling: Explain how reactive ordering differs from a typical sequence of instructions and why separating causes from effects can help or hurt readability.
Why it matters: This contrast helps you judge when reactive ordering makes code clearer rather than assuming it always will. - Pipelines of actions and calculations: Describe the kind of composition reactive architecture allows and how it compares with chaining data transformations.
Why it matters: It connects reactive architecture to the pipeline-style composition you have already seen. - Timeline flexibility and risk: Explain the potential benefit and danger of the timeline flexibility gained by reversing how ordering is expressed.
Why it matters: Timeline flexibility is useful only when the possible orderings still meet the application's needs.
Key terms: reactive architecture, causes, effects, pipelines, actions, calculations, timeline, possible orderings
18.5 Cells are first-class state
This section turns the shopping cart’s global mutable variable into a ValueCell so reads and updates become explicit operations that can be controlled.
- First-class shopping cart state: Explain why the shopping cart variable is wrapped in a ValueCell and describe how val() and update() replace direct reads and assignments.
Why it matters: Explicit operations provide a place to control access to the cart as the design develops.
Key terms: first-class state, ValueCell, val(), update(), shopping_cart
18.6 We can make ValueCells reactive
This section makes ValueCells reactive by introducing watchers that run when cell state changes.
- ValueCell watchers and changes: Explain that a ValueCell can have watcher functions that are called every time its state changes, allowing the cart to trigger an update to the shipping icons.
Why it matters: Watchers let code respond to state changes without putting the response in the description of the change itself.
Key terms: ValueCell, watchers, listeners, callbacks, observers, event handlers
18.7 We can update shipping icons when the cell changes
Shows how watching the shopping cart cell keeps shipping icons current without updating them in every handler.
- Shipping icon watcher: Explain how adding update_shipping_icons() as a watcher of the shopping_cart ValueCell changes when the icons are updated and what handlers no longer need to do.
Why it matters: One watcher keeps the icons in sync even when the cart changes through different handlers.
Key terms: ValueCell, watchers, update_shipping_icons(), shopping_cart
18.8 FormulaCells calculate derived values
The section shows how FormulaCells keep derived values current and let actions respond when those values change.
- FormulaCell derived values: Explain how a FormulaCell keeps a value derived from an upstream cell up to date and why its value is not updated directly.
Why it matters: This explains how a derived value stays in sync without a separate direct update. - Watching derived totals: Describe how watching a FormulaCell lets actions respond to changes in a derived total in the cart example.
Why it matters: Watching the derived total lets the cart's changes drive the relevant DOM updates.
Key terms: FormulaCells, upstream cell, derived value, watchers, total, DOM
18.9 Mutable state in functional programming
This section explains why functional programs still need mutable state and how ValueCells can keep that state valid without guaranteeing update order.
- Mutable state and relative safety: Explain why functional software still needs mutable state and why cells holding immutable values are safer than regular global variables.
Why it matters: This keeps the advice to avoid mutable state from becoming an impractical absolute rule. - ValueCell validity through updates: Describe how ValueCell initialization and update() can keep stored values valid, and distinguish that guarantee from ordering guarantees across timelines.
Why it matters: It shows what safety a ValueCell provides—and what concurrency problem it does not solve.
Key terms: mutable state, cells, immutable values, global variables, ValueCell, update(), calculation, valid value, timelines
18.10 How reactive architecture reconfigures systems
The section characterizes the reconfigured reactive code as change handlers and classifies mutable cells as actions.
- Change handlers in reactive code: Describe the reconfiguration shown here: each part of the code becomes a handler for something else changing.
Why it matters: This gives you the basic shape of the reactive system before examining its consequences. - Mutable cells as actions: Explain why ValueCell and FormulaCell count as actions rather than calculations or data.
Why it matters: It connects reactive cells to the book’s distinction between actions, calculations, and data.
Key terms: reactive architecture, handler, ValueCell, FormulaCell, actions, mutable state, .val(), .update()
18.11 Decouples effects from their causes
This section explains how reactive architecture updates shipping icons when the cart changes, regardless of what caused the change.
- Cause-independent cart updates: Explain why updating shipping icons in every cart-related button handler couples the update to particular causes, and how reactive architecture expresses the rule once for any change to the global cart.
Why it matters: One cart-change rule covers adding, removing, and emptying the cart without repeating the icon update in each handler.
Key terms: reactive architecture, cause, effect, global cart, shipping icons
18.12 Decoupling manages a center of cause and effect
The section explains how decoupling cart changes from their effects reduces maintenance work, and when that separation is useful.
- Cart cause-and-effect coupling: Explain why handling every cart change together with every resulting action creates growing maintenance work, and how separating causes from effects changes that growth.
Why it matters: This explains why separating cart changes from their effects can make a changing system easier to maintain. - When decoupling helps: Distinguish a center of cause and effect that benefits from decoupling from a straightforward sequence of actions that may be clearer written in order.
Why it matters: Decoupling is useful for a particular maintenance problem, not a rule to apply to every sequence of actions.
Key terms: causes, effects, center of cause and effect, decoupled, decoupling
18.13 Treat series of steps as pipelines
Explains how reactive architecture composes steps into data-flow pipelines, when that pattern fits, and which tools support different kinds of flow.
- Pipeline composition and fit: Explain how a pipeline composes actions and calculations by passing one step’s output to the next, and when that pattern is or is not appropriate.
Why it matters: Recognizing the data flow helps you decide whether a pipeline fits a series of steps. - Single values and event streams: Distinguish the tools the section names for a single value, a stream of events, and reactive communication between services.
Why it matters: The kind and scale of data flow affect which pipeline tool is suitable.
Key terms: reactive architecture, pipeline, actions, calculations, promises, ReactiveX, streams, RxJS, Kafka, RabbitMQ
18.14 Flexibility in your timeline
The section explains when reactive architecture makes timelines easier to manage and when its flexibility is useful.
- Timeline flexibility without shared resources: Explain how reactive architecture splits work into shorter timelines and why those timelines are manageable in the shopping cart example.
Why it matters: This explains why splitting a timeline can make concurrent work simpler rather than more complicated. - Choosing reactive architecture: Distinguish a situation suited to reactive architecture from one better expressed as a straightforward sequence of actions, using the two examples in the section.
Why it matters: It helps you choose reactive architecture for flexibility where it is useful, rather than applying it to every workflow.
Key terms: reactive architecture, timelines, shared resources, watcher functions, ValueCell, FormulaCell, causes, effects, center of cause and effect
18.15 Two separate architectural patterns
This section distinguishes reactive and onion architectures by their roles and scale, and explains how they can be used together.
- Reactive and onion architecture roles: Distinguish the reactive architecture’s focus on the order of actions from the onion architecture’s role in structuring an entire service that interacts with the outside world. Explain that reactive architecture may be nested inside onion architecture, but neither requires the other.
Why it matters: Keeping their different roles in mind helps you see how the architectures can complement each other without confusing them.
Key terms: reactive architecture, onion architecture
18.16 What is the onion architecture?
Introduces onion architecture as concentric layers for software that interacts with the world, and states its rules for interaction and inward calls.
- Onion architecture layer roles: Describe the three general layer groupings and the kind of work each contains. Explain that these are typical groupings, not a required set of exact layers.
Why it matters: The layer groupings give you a way to place different kinds of code in a system that interacts with the world. - Inward calls and interaction boundary: Explain the two rules for how layers interact: outside-world interaction stays in the interaction layer, and layers call inward without knowing about layers outside themselves.
Why it matters: These rules explain how the concentric-layer picture constrains a system’s code.
Key terms: interaction layer, domain layer, language layer, layers call in toward the center
18.17 Review: Actions, calculations, and data
Reviews actions, calculations, and data, then explains why extracting calculations from actions naturally leads toward an onion architecture.
- Actions calculations and data: Distinguish inert data, repeatable calculations, and actions whose behavior depends on the outside world or on when they run.
Why it matters: These distinctions guide choices about how to structure a functional system. - Extraction and architectural shape: Explain why extracting calculations from actions can naturally produce something like the onion architecture, and state what the name is useful for here.
Why it matters: It connects the earlier refactoring practice to a way of viewing service architecture.
Key terms: data, calculations, actions, timelines, extract calculations from actions, onion architecture
18.18 Review: Stratified design
Reviews how stratified design organizes functions and shows why actions spread upward through a call graph.
- Functions arranged in layers: Explain that stratified design arranges functions in layers according to which functions they call and which functions call them, helping clarify reuse, changeability, and testing priorities.
Why it matters: The layers give you a way to reason about a function’s role in the design. - Actions spreading up the graph: Trace the spreading rule in a stratified-design graph: when a box is an action, every box on its path to the top is also an action.
Why it matters: The graph makes it visible how an action affects the functions that depend on it.
Key terms: stratified design, layers, spreading rule, action, graph
18.19 Traditional layered architecture
This section describes a traditional layered web API and explains why putting the database at its foundation makes the stack nonfunctional in the book’s sense.
- Traditional web API layers: Describe the web interface, domain, and database layers and how they depend on one another in the traditional layout.
Why it matters: The dependency order explains the section’s critique of this architecture. - Database foundation and actions: Explain why the section does not regard the traditional database-founded stack as functional, despite its possible use of calculations.
Why it matters: It identifies the functional-design problem with this otherwise common and successful layout.
Key terms: web interface layer, domain layer, database layer, actions, calculations, functional architecture
18.20 A functional architecture
Contrasts traditional and functional architectures, then states how the functional design becomes an onion architecture.
- Database placement and domain logic: Explain why a functional architecture moves the mutable database out of the layer stack and builds business and domain logic as calculations, with an action connecting those rules to database state.
Why it matters: Database placement affects whether domain rules can remain calculations. - Onion architecture layer rules: Describe the onion architecture rules given here: world interaction belongs in the interaction layer, calls go inward, and layers do not know about layers outside themselves.
Why it matters: These rules explain how the functional architecture’s separation is maintained across layers.
Key terms: database, actions, calculations, domain operations, onion architecture, interaction layer, layers
18.21 Facilitating change and reuse
Explains how onion architecture makes infrastructure easier to change and domain logic easier to reuse by keeping external-service calls out of the domain layer.
- Infrastructure change and domain reuse: Explain why onion architecture makes external services easier to change and domain calculations easier to reuse and test.
Why it matters: This explains which changes the architecture is designed to make easy. - Shopping cart layer responsibilities: Trace how the shopping-cart total example divides work between the interaction and domain layers, and contrast it with the typical architecture.
Why it matters: The example shows that separating domain logic from infrastructure changes where work happens, not what the application does.
Key terms: onion architecture, interaction layer, domain layer, external services, cart_total(), web handler, calculation
18.22 Examine the terms used to place the rule in a layer
This section uses the terms in a rule to distinguish domain logic from technical logic that belongs in the interaction layer.
- Domain terms and layer placement: Explain how the terms used in a rule help determine whether it is a domain rule or belongs in the interaction layer. Apply the distinction to database fallback and web-request retries.
Why it matters: Business importance alone is not a reliable guide to where logic belongs.
Key terms: domain rule, business rule, domain terms, interaction layer
18.23 Analyze readability and awkwardness
This section weighs the ideal onion architecture against readability, delivery speed, and performance, then applies its layer boundaries to concrete examples.
- Pragmatic architectural tradeoffs: Explain why making every domain operation a calculation is not always the clearest immediate choice, and describe how to handle compromises made for readability, development speed, or performance.
Why it matters: It helps you improve the architecture without sacrificing clarity or ignoring practical constraints. - Report database access boundary: Explain why fetching discounts inside the report's reduce callback would change the status of generateReport(), and where the database access should go instead.
Why it matters: A new data requirement need not turn report generation into a database-dependent action. - Library software layer placement: Distinguish interaction, domain, and language-layer functionality using the public-library examples in the exercise.
Why it matters: Classifying familiar functions makes the layer boundaries more concrete.
Key terms: onion architecture, calculations, actions, interaction layer, domain layer, generateReport(), reduce, action, language layer
18.24 Conclusion
The conclusion distinguishes reactive architecture’s way of sequencing actions from onion architecture’s emergence through functional programming practices.
- Reactive action sequencing: Explain that reactive architecture specifies which actions happen in response to another action, reorienting how actions are sequenced.
Why it matters: This captures the chapter’s high-level distinction between reactive architecture and other ways of organizing actions. - Onion architecture perspective: Explain that onion architecture arises naturally from applying functional programming practices and offers a perspective that appears at every level of code.
Why it matters: It explains why the chapter presents onion architecture as a broadly useful perspective rather than just a pattern for one part of a system.
Key terms: reactive architecture, actions, onion architecture, functional programming practices
18.25 Summary
18.26 Up next…
The section closes part 2 and previews a concluding chapter about what readers have learned and where to go next.
Where this is used later
- Big takeaways (chapter 19) · A functional architecture
Concepts explained in this chapter
- mathematical calculationA calculation is a category of code that deterministically computes a result from its inputs without directly interacting with the outside world.
- reactive architectureA software architecture pattern centered on asynchronous communication and systems that respond to changing events or conditions.
- domain layerThe architectural layer containing domain rules and calculations while remaining independent of external services.
- actionAn action is a computation whose observable result depends on when or how it is performed and may involve effects beyond producing a value.
- value cellA book-specific reactive container that holds a value and propagates notifications when its value is available or changes.
- validation ruleA rule that specifies behavior or constraints within the problem domain modeled by software.
- code readabilityThe degree to which source code can be understood by human readers.
- functional, stateless philosophyA programming paradigm that emphasizes composing computations as evaluations of functions while minimizing mutable state and side effects.
- side effectAn observable interaction with state or the external environment that occurs in addition to producing a computation's return value.
- immutable collectionData or state whose represented values cannot be modified after creation.
- dataAn inert representation of information that requires interpretation to acquire meaning and can be stored or transmitted.
- action spreading ruleA rule stating that action status propagates upward through caller paths in a function call graph.
- stratified designAn architectural pattern that organizes software into layers ordered from relatively stable foundations to more frequently changing elements.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
