skip to content

Application Architecture Patterns (PoEAA)

Fowler's Patterns of Enterprise Application Architecture: the standard vocabulary for organizing domain logic, mapping objects to relational data, defining a service boundary, and moving data across it. Most frameworks you use are implementations of these, so knowing the names explains a lot of behavior.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

page 1 of 2

In Martin Fowler's data source architectural patterns, what is the Active Record pattern, and what two responsibilities does a single Active Record object combine into one class?

level: juniorimportance: must knowfreq 70%

answer

  1. one class, one row, self-saving
  2. combines data + behavior + persistence
  3. 1:1 class-to-table mapping
  4. fat model / Rails ActiveRecord
  5. hard to unit-test without a DB

basics

~10 s

Active Record is a design where one class both holds the data fields of a database row (like name, price) and has methods to save, load, and delete itself from that row's table.

solid answer

~30 s

Active Record is an object that wraps a single row of a database table, exposing that row's columns as fields and adding save(), delete(), and finder methods directly on the object. It combines domain data and simple business logic with persistence logic in one class, mapped 1:1 to a table. It's simple and fast to build for CRUD-heavy, low-complexity domains, but it couples your domain objects directly to the database schema.

go deeper

for a junior

Can describe that Active Record objects both hold data and know how to save/load themselves; may not yet see the testing or coupling downsides.

for a middle

Explains the 1:1 table-to-class coupling and can name at least one concrete downside (testability, fat models).

for a senior

Discusses when Active Record is the right fit (simple CRUD, low domain complexity) versus when it breaks down, and can compare it against Data Mapper/Table Data Gateway.

for a principal

Frames the choice as an architectural decision tied to team size, domain complexity growth trajectory, and migration cost — recognizes it as a deliberate simplicity/coupling trade rather than a technology limitation.

## What an Active Record object is The **Active Record** pattern, as codified in Martin Fowler's *Patterns of Enterprise Application Architecture*, describes an object that wraps one row of a database table and puts both the data and the logic to read and write that row on the same class. Concretely, a class like `Product` has: - **instance fields** mirroring the table's columns (`name`, `price`, `sku`); - **instance methods** like `save()`, `update()`, and `delete()` that run the corresponding SQL for that specific row; - **class-level or static finder methods** like `Product.find(id)` or `Product.findAll()` that query the table and return fully-populated instances. Any business rules that apply to a product — say, a discount calculation or a validation that price can't be negative — typically live as additional instance methods on the very same class. There is no separate layer standing between the object a developer manipulates in memory and the row that object represents in the database. ## Why it exists This pattern exists to minimize indirection. When a domain concept maps cleanly onto a single table, and the business logic attached to it is modest, introducing a separate mapping layer is pure overhead: extra classes, extra configuration, extra places to look when tracing a bug. Active Record collapses domain representation and persistence into one place so a developer reading the `Product` class sees everything about how a product behaves and how it's stored in one file. This is precisely why it became the default in frameworks aimed at getting CRUD-heavy applications running quickly: - **Ruby on Rails'** ActiveRecord ORM, most famously; - **PHP's Laravel** (Eloquent), later adopted in similar form; - **Django's** model layer, more loosely. ## The trade-off The trade-off is **coupling**. Because `save()` and `find()` are literally methods on the domain object, that object cannot exist, in any meaningful sense, independent of a database connection and a schema. Business logic that lives alongside those methods inherits that dependency: - you cannot easily construct a `Product` in a unit test and exercise its discount-calculation method without either running against a real (or in-memory) database or mocking out the persistence methods on the very class you're testing; - the class also violates single-responsibility in the classic sense — it has at least two reasons to change: a change to a business rule, and a change to the database schema. For a simple domain this rarely bites; for a growing one, it does. ## Failure modes 1. **The 'fat model' problem.** In production, the most common failure mode is what practitioners call the 'fat model' problem: as an application grows, more and more logic gets bolted onto the Active Record class because it's the path of least resistance — it's already there, already has the data loaded, and adding one more method is easier than designing a new collaborator. Over time a class meant to represent one row in one table accumulates validation rules, notification logic, formatting helpers, and authorization checks, becoming hundreds of lines that mix unrelated concerns and resist safe refactoring, since so much of the codebase reaches into it directly. 2. **The second table.** A second, related failure mode surfaces when the domain concept the class represents doesn't actually correspond to one table any longer — for instance once an `Order` needs line items from a second table — and developers either bolt ad hoc loading code onto the `Order` class or begin manually wiring multiple Active Record objects together, both of which erode the pattern's original simplicity. ## Where it shows up A concrete real-world illustration: Ruby on Rails applications are built around ActiveRecord by default, and the well-documented 'fat model, skinny controller' guidance that Rails teams eventually adopt is a direct response to this exact failure mode — pushing logic out of Active Record classes into separate service or concern objects once a model's responsibilities outgrow a single row's worth of behavior. The pattern itself isn't wrong; it's a deliberate trade of long-term flexibility for short-term simplicity, and understanding when that trade stops paying off — usually when business logic complexity or the object's table-shape stops being simple — is the actual skill being tested when this pattern comes up in an interview.

  • How does Active Record typically implement finder methods like findById — as instance methods or class-level/static methods?
    As class-level or static methods, since you don't yet have an instance when you're trying to look one up; the class itself acts as the entry point for queries, while instance methods like save() and delete() operate on an already-loaded row.
  • What happens to the Active Record pattern when a domain object needs to be assembled from multiple tables, e.g., Order and OrderLines?
    It strains: the Order class either grows extra ad hoc loading and saving logic to stitch the related table together, or developers wire several Active Record objects to call each other's save/delete methods in a fragile order, which blurs the pattern's original 1:1 class-to-table simplicity.
  • Why is a class built around the Active Record pattern hard to unit-test in isolation from the database?
    Because persistence calls are hard-coded as methods directly on the class holding the business logic, so exercising a business rule also exercises save/find machinery on the same object; tests end up needing a real or in-memory database, or heavy mocking of the object's own persistence methods.

Like a filing folder that also knows how to file and retrieve itself from the cabinet — the folder (data) and the filing clerk (persistence) are the same object.

saying these in an interview costs you the question

  • Says Active Record and Data Mapper are the same thing
  • Doesn't mention the 1:1 class-to-table coupling
  • Thinks Active Record is only a Rails-specific concept, not a general PoEAA pattern
  • Can't explain why it makes unit testing without a database hard
  • Believes Active Record handles complex multi-table domain logic well

context

open as a page

What is the Transaction Script pattern for organizing business logic, and what does a single script typically do?

level: juniorimportance: must knowfreq 55%

basics

~20 s

A Transaction Script is one procedure that handles a single business request end to end: it reads input, runs the rules step by step, talks to the database, and returns a result. No shared object model, just a top-to-bottom recipe per action.

open as a page

What is a Data Transfer Object (DTO), and why would a service return a DTO instead of directly serializing its internal domain or persistence entity objects to a client?

level: juniorimportance: must knowfreq 85%

basics

~20 s

A DTO is a plain object built just to carry data across a boundary, like a network call. Services return DTOs instead of internal objects so clients don't depend on internal details that might change, break, or leak sensitive data.

open as a page

In enterprise integration, three common message types are commands, events, and queries. What distinguishes them, and how does each affect coupling between sender and receiver?

level: juniorimportance: must knowfreq 55%

basics

~20 s

A command tells one system to do something specific, like 'ship this order.' An event announces that something already happened, like 'order shipped,' and anyone can listen. A query just asks for data without changing anything. Each creates a different kind of dependency between sender and receiver.

open as a page

In plain terms, what is the Lazy Load pattern, and why would a system delay loading an object's data instead of fetching everything up front?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Lazy Load means you don't fetch data until you actually need it. Instead of loading a whole object graph at once (which can be slow and wasteful), you load the minimum needed now and fetch the rest later, only if code actually asks for it.

open as a page

What problem does the Identity Field pattern solve when mapping a database row to an in-memory object, and why can't you just rely on the object's own reference identity or equals() instead?

level: juniorimportance: must knowfreq 65%

basics

~20 s

Identity Field stores the database primary key inside the object so the mapper always knows which row that object represents, because loading the same row twice can create two different object instances that need to be recognized as the same thing.

open as a page

What is the Repository pattern, and what problem does it solve for code that needs to save and load domain objects?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A Repository is an object that lets your business code save and fetch domain objects using simple method calls, like a collection, without knowing whether the data lives in a SQL database, a file, or memory.

open as a page

What is a Service Layer in a typical multi-layer application, and what problem does it solve for the code that calls into the business logic?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A Service Layer is a thin layer of methods that sit in front of the business logic. Instead of each screen or API endpoint talking to domain objects and the database directly, it calls one Service Layer method that does the whole job for it.

open as a page

What is the Unit of Work pattern, and what problem does it solve when an application needs to save changes to a database?

level: juniorimportance: must knowfreq 70%

basics

~20 s

It's an object that remembers every change you made to your data - new records, edits, deletions - while you work, then writes them all to the database together in one go, instead of saving each change separately.

open as a page

What is the structural difference between the Row Data Gateway and Table Data Gateway patterns, and how does that difference affect how many gateway objects exist at runtime for a table with 10,000 rows?

level: middleimportance: must knowfreq 60%

basics

~20 s

Row Data Gateway makes one gateway object per database row (so 10,000 rows means up to 10,000 objects), while Table Data Gateway makes one single object for the whole table that you call with row IDs as parameters.

open as a page

What specific problem does the Data Mapper pattern solve that Active Record does not, and what's the concrete cost of adopting Data Mapper instead of Active Record?

level: middleimportance: must knowfreq 65%

basics

~20 s

Data Mapper keeps your business objects completely unaware of the database — a separate 'mapper' class does all the saving and loading — so your business logic stays clean and testable, but you now have to write and maintain that extra mapping layer.

open as a page

How does the Domain Model pattern differ from Transaction Script in organizing business logic, and what should actually drive the choice between them?

level: middleimportance: must knowfreq 65%

basics

~20 s

Domain Model puts business rules inside objects that represent real concepts, like an Order that knows how to calculate its own total, while Transaction Script keeps rules in separate step-by-step procedures. Pick Domain Model when rules are complex and interact a lot; pick Transaction Script when they're simple.

open as a page

What does the Assembler component do in the DTO/Assembler pattern, and where should the mapping logic between a domain object and its DTO live — on the domain object, in the controller, or in a dedicated mapper class?

level: middleimportance: must knowfreq 75%

basics

~20 s

The Assembler is the piece of code that copies data between a domain object and its DTO, in both directions. It should live in its own small class, not inside the domain object or the web controller, so each side stays focused on its own job.

open as a page

What is the difference between a point-to-point messaging channel and a publish-subscribe channel, and how does that choice affect how many consumers can safely process the same message?

level: middleimportance: must knowfreq 60%

basics

~20 s

A point-to-point channel delivers each message to exactly one consumer, even if several are listening — good for work that should happen once. A publish-subscribe channel delivers a copy of each message to every subscriber, good for broadcasting facts to many independent listeners.

open as a page

In an asynchronous request/response flow over messaging, what problem does a correlation ID solve, and where does it need to be carried?

level: middleimportance: must knowfreq 65%

basics

~20 s

When messages travel through queues instead of direct calls, a reply can arrive long after (and out of order from) the request, so you need a shared ID stamped on both to know which reply answers which request.

open as a page

PoEAA describes four ways to implement Lazy Load: lazy initialization, virtual proxy, value holder, and ghost. Concretely, how does each of these mechanisms work, and what distinguishes one from another?

level: middleimportance: must knowfreq 55%

basics

~20 s

Lazy initialization checks a flag before loading. A virtual proxy is a stand-in object that loads the real one on first use. A value holder is a small wrapper that loads its value on request. A ghost is a mostly-empty real object that fills itself in when any field is touched.

open as a page

In an ORM's Foreign Key Mapping pattern, how do you represent a one-to-many (or many-to-one) association between two tables using a foreign key column, and what problem arises when a child object must be saved before its parent has a database-assigned primary key?

level: middleimportance: must knowfreq 80%

basics

~20 s

Foreign Key Mapping stores the parent's ID as a column on the child's table (or an in-memory reference resolved to that column), so 'many' rows point back to 'one' row. The problem is you can't put a real parent ID on the child until the parent has actually been saved and been given one.

open as a page

A web app loads a list of 50 orders, then in a template loop calls order.getLineItems() for each one, and the ORM fires 50 separate SELECT statements plus the original query. What is this problem called, why does lazy loading cause it, and how would you fix it without simply switching every association to eager loading?

level: middleimportance: must knowfreq 90%

basics

~20 s

This is the N+1 selects problem: one query loads the list, then one extra query per item fetches its related data, because each lazy association is fetched separately the moment it's touched. Fix it by fetching the related data in one batched query up front, not by making everything eager everywhere.

open as a page

A Repository interface exposes methods like findActiveCustomersInRegion(region) instead of letting callers build SQL WHERE clauses themselves. What is this technique called, and what problem does it prevent?

level: middleimportance: must knowfreq 65%

basics

~20 s

It's called query encapsulation: the Repository hides the actual query logic behind a named method, so callers just say what they want, not how to fetch it, and can't accidentally write unsafe or database-specific queries.

open as a page

PoEAA's Service Layer pattern describes two variants for implementing service methods: the operation script style and the domain facade style. What distinguishes them, and how would you choose between them for a given use case?

level: middleimportance: must knowfreq 75%

basics

~20 s

Operation script means the service method itself contains the step-by-step logic for the use case. Domain facade means the service method is thin and mostly just calls rich behavior already living on domain objects. Pick domain facade when the logic is reused or complex; pick operation script when the use case is simple and one-off.

open as a page

How does a Service Layer typically manage the transaction boundary for a use case that touches several different domain objects or repositories, and what goes wrong if that boundary is drawn in the wrong place?

level: middleimportance: must knowfreq 65%

basics

~20 s

The Service Layer method usually opens one transaction at the start of the use case and commits it at the end, so all the changes it makes either all succeed or all fail together. If it opens too many small transactions, you can end up with half-finished work; if one transaction is too big, it can lock too much data for too long.

open as a page

How does a Unit of Work typically detect which loaded objects are 'dirty' (changed) so it knows what to write back to the database, and what role does the identity map play in that?

level: middleimportance: must knowfreq 65%

basics

~20 s

It keeps a copy of each object's data from when it was loaded, then compares that copy to the current object right before saving; anything different gets an UPDATE. The identity map makes sure there's only one copy of each record in memory to compare.

open as a page

What is an Anemic Domain Model, why did Martin Fowler call it an anti-pattern, and what does the trade-off actually look like in practice?

level: seniorimportance: must knowfreq 60%

basics

~20 s

An anemic domain model is when your 'domain' objects are just data bags with getters and setters and no real logic, while all the actual business rules live in separate service classes. It looks object-oriented but behaves like plain procedural code.

open as a page

Why does an idempotent receiver need to detect duplicate messages explicitly, rather than relying on the messaging channel to guarantee exactly-once delivery?

level: seniorimportance: must knowfreq 70%

basics

~20 s

Most real messaging systems can only promise 'at least once' delivery, so the same message can arrive twice (e.g., after a retry). The receiver has to notice a duplicate itself and skip re-doing the work, or side effects like double-charging happen.

open as a page

A service loads 100 orders, then for each order calls a lazily-loaded getLineItems() to compute a total. The endpoint that used to take 50ms now takes 4 seconds under real data. Walk through why lazy loading causes this, and how you'd fix it.

level: seniorimportance: must knowfreq 80%

basics

~20 s

Lazy loading fetches each order's line items only when asked. Looping over 100 orders and asking each one for its items triggers 100 separate database round-trips (plus the original 1 to get the orders) instead of one combined query — that's the classic N+1 problem.

open as a page

A web app loads an Order inside a request-scoped database session, returns it to a view-rendering layer, and closes the session before rendering happens. Rendering then touches a lazy-loaded field and the app throws at runtime. What's happening, and what are the standard ways to prevent it?

level: seniorimportance: must knowfreq 65%

basics

~20 s

The lazy field needs an active connection/session to fetch its data. By the time rendering happens, that connection is already closed, so trying to load the field fails with an error instead of quietly returning data.

open as a page

An ORM needs to map a class hierarchy - an Employee base class with Salaried and Hourly subclasses - onto relational tables. Compare Single Table Inheritance, Class Table Inheritance, and Concrete Table Inheritance for doing this, including what happens to nullable columns, joins, and polymorphic queries in each.

level: seniorimportance: must knowfreq 70%

basics

~20 s

Single Table Inheritance puts every subclass's columns in one wide table with lots of nulls. Class Table Inheritance splits shared and subclass-specific columns into separate tables joined by shared ID. Concrete Table Inheritance gives each subclass its own fully self-contained table, duplicating the shared columns.

open as a page

Should a Repository interface return database-framework types like an ORM's managed entity proxy or a framework's Page<T> pagination wrapper, or should it return plain domain objects? What breaks if persistence-framework types leak through the interface?

level: seniorimportance: must knowfreq 55%

basics

~10 s

A Repository should return plain domain objects, not framework-specific wrappers. If it leaks framework types, callers end up depending on that framework everywhere, which defeats the whole point of hiding persistence details.

open as a page

When a Unit of Work commits, how does it decide the order in which INSERT, UPDATE, and DELETE statements are executed, and why does that ordering matter?

level: seniorimportance: must knowfreq 55%

basics

~20 s

It has to insert parent records before child records that reference them, and delete child records before their parents, otherwise the database rejects the write for violating a foreign key rule. The Unit of Work sorts writes to respect those dependencies before sending them.

open as a page

What is the Table Module pattern for organizing domain logic, and in what situations does it fit better than Transaction Script or Domain Model?

level: middleimportance: should knowfreq 30%

basics

~20 s

Table Module has one class per database table, not per row, and it holds the business logic for that table. You pass it a set of rows, like a whole table's worth of data, instead of creating a separate object for each individual record.

open as a page

showing 1–30 of 52