Paradigms, Architecture, and Design Trade-offs
Paradigms guide the expression of local computation.
19.1 Paradigm versus architecture
Paradigms guide the expression of local computation. Architecture describes larger system boundaries, dependencies, and deployment. A system may use OOP internally, functional transformations in its domain core, SQL declaratively, and event-driven communication between services.
19.2 Functional core, imperative shell
Use pure functions for domain rules and isolate I/O in adapters. The core can be tested with ordinary values; the shell handles persistence, network calls, clocks, and user interaction.
19.3 Hexagonal architecture
Ports define the capabilities a domain/application component needs; adapters implement those ports for a database, web framework, queue, or external service. This reduces coupling to infrastructure but should not become unnecessary abstraction for every trivial function.
19.4 Domain-driven design (DDD)
DDD models complex business domains with explicit concepts, language, invariants, and boundaries. Concepts include entities, value objects, aggregates, repositories, domain services, and bounded contexts. DDD is not itself a programming paradigm, but it influences how OOP, functional modeling, and module boundaries are used.
19.5 CQRS
Command Query Responsibility Segregation separates operations that change state from operations that read state. It can help when read and write workloads or models differ significantly, but it adds complexity and may introduce eventual consistency. It is not required for every application.
19.6 Functional versus object-oriented design
Functional approach often emphasizes OOP approach often emphasizes Functions and transformations Objects and contracts Immutable values where practical Encapsulated object state Composition and higher-order functions Interfaces, delegation, inheritance Result/Option-style values or effects
Exceptions, result types, object contracts Immutable data and explicit effects Encapsulation, synchronization, actors
Transformations and explicit domain rules Stateful entities and interchangeable components
These are tendencies, not strict boundaries. Good systems often combine them.
19.7 Performance trade-offs
Algorithmic complexity first Allocation and garbage collection Memory layout and cache behavior I/O and network latency Lock contention and task scheduling JIT warm-up and runtime optimization Database query plans Profiling and representative workloads
Do not assume one paradigm is inherently fastest. Performance depends on implementation, data, runtime, compiler, hardware, and workload.
19.8 Testability and correctness
Pure functions are often easy to unit-test. Object boundaries can support substitutable fakes. Declarative queries can be validated against fixtures and integration tests. Concurrent systems need stress tests, race detection, deterministic scheduling techniques where available, and clear invariants.
Exercise
Design a course-enrollment system in three ways:
A procedural module An object-oriented domain model A functional core with imperative adapters
Compare invariants, test setup, dependencies, and change cost.
Quiz
? What is "functional core, imperative shell"?
+ Pure functions hold the domain rules; an outer layer performs I/O and other effects. :: The core is testable with plain values.
- All code is written in a functional language. :: It is an architecture, usable in any language.
- The shell is written in Bash. :: "Shell" means the effectful outer layer.
- Effects are forbidden. :: Effects are moved to the boundary, not removed.
? When is CQRS worth its complexity?
+ When read and write workloads or models differ significantly. :: It separates them so each can be optimised.
- In every application. :: The module says it is not required for every application.
- When there is only one database table. :: The benefit comes from differing models.
- When eventual consistency must be avoided. :: CQRS may introduce eventual consistency.
? Which are reasonable performance considerations before blaming a paradigm? (select all that apply)
+ Algorithmic complexity :: Always first.
+ Allocation, garbage collection and memory layout :: These dominate many workloads.
+ Profiling with representative workloads :: Evidence beats assumption.
- The paradigm's name :: No paradigm is inherently fastest.