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?
answer
- row-per-object vs table-per-object
- no business logic in either gateway
- Row Data Gateway pairs with Domain Model
- Table Data Gateway pairs with Transaction Script
- one stateless instance vs many instances
basics
~20 sRow 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.
solid answer
~40 sRow Data Gateway is an object-per-row: each instance wraps one row's data as fields plus insert/update/delete for that row, so you instantiate one per row you're working with. Table Data Gateway is a single, often stateless, class per table: it has no per-row fields, and every method (find, insert, update, delete) takes the row's identifying data as parameters and returns plain records (e.g., a map or DTO) rather than domain objects. Both are pure data-access gateways with no business logic — that's what separates either from Active Record. Row Data Gateway suits richer per-row objects paired with a Domain Model; Table Data Gateway suits Transaction Script-style procedural code.
go deeper
Can state that one pattern is per-row and the other is per-table, without necessarily explaining the runtime memory or pairing implications.
Explains that neither gateway carries business logic and correctly pairs Row Data Gateway with Domain Model, Table Data Gateway with Transaction Script.
Weighs the memory/allocation cost of Row Data Gateway at scale versus Table Data Gateway's raw-record shuttling cost, and picks based on the surrounding architecture.
Ties the choice to team-wide architectural conventions (whether the codebase is Domain-Model-centric or Transaction-Script-centric) and flags when introducing the 'wrong' gateway style creates friction against the dominant pattern already in use.
## What a single instance represents Row Data Gateway and Table Data Gateway are both **pure data-access patterns from PoEAA** — unlike Active Record, neither one holds business logic — but they differ in what a single instance represents. - **A Row Data Gateway** object wraps exactly one row: it has instance fields for that row's columns and instance methods (`insert()`, `update()`, `delete()`) that act on that specific row, so working with a result set of 500 rows means instantiating 500 gateway objects, one per row, each independently responsible for persisting itself. - **A Table Data Gateway**, by contrast, is a single class representing the whole table, usually with no per-row state at all; you call one shared instance's methods — `findById(id)`, `insert(rowData)`, `update(id, rowData)` — passing in whatever data identifies or describes the row, and it hands back plain records (a map, an array, a lightweight DTO) rather than a typed row object. For a table with 10,000 rows, Row Data Gateway usage could mean up to 10,000 short-lived objects in memory during a batch operation, while Table Data Gateway usage means exactly one gateway instance regardless of how many rows you touch. | Dimension | Row Data Gateway | Table Data Gateway | |---|---|---| | A single instance | wraps exactly one row | represents the whole table | | Hands back | a typed row object | plain records: a map, an array, a lightweight DTO | | For a table with 10,000 rows | up to 10,000 objects | exactly one gateway instance | | Pairs naturally with | a Domain Model | Transaction Script | ## Why both exist The reason both patterns exist as distinct options is to give the rest of the codebase a clean seam for pure data access without forcing business logic to live in the same class — the opposite trade Active Record makes. - **Row Data Gateway** is meant to pair with a separately-defined Domain Model: you'd have a rich `Order` class holding business rules, and a companion `OrderRowGateway` handling the actual SQL, with the `Order` delegating to the gateway rather than doing SQL itself. - **Table Data Gateway** pairs naturally with Transaction Script — code organized as a sequence of procedures per business transaction rather than behavior-rich objects — since Transaction Script just needs a straightforward way to read and write rows without any notion of object identity per row. ## The trade-off The trade-off between the two is largely about **scale and shape**. - Row Data Gateway gives you a typed object per row that's easy to pass around and matches naturally with a domain object that also represents one row, but instantiating one object per row becomes expensive for large scans: a report touching hundreds of thousands of rows creates hundreds of thousands of gateway objects, adding real allocation and garbage-collection pressure for no benefit if nothing besides simple reads is happening. - Table Data Gateway avoids that churn entirely — one instance serves arbitrarily many rows — but the caller has to work with loosely-typed records rather than a well-typed row object, pushing more responsibility for interpreting field names and types onto calling code, and losing some of the discoverability an IDE gives you with a typed class. ## Failure modes The failure modes follow directly from misapplying either one. 1. **Row Data Gateway in a hot path.** Reaching for Row Data Gateway in a hot, high-volume batch or reporting path is the classic performance trap: profiling such code often reveals object-allocation overhead entirely attributable to instantiating one wrapper per row rather than to the actual database query. 2. **Table Data Gateway against a rich domain.** In the other direction, reaching for Table Data Gateway when the surrounding code is organized as a rich Domain Model creates friction, since the domain objects want to work with typed collaborators, not raw maps, forcing extra translation code at every call site — effectively reinventing Row Data Gateway or Data Mapper piecemeal. ## Where it shows up A concrete real-world echo of this split shows up in older enterprise Java and .NET systems built before ORMs like Hibernate or Entity Framework were dominant: .NET's ADO.NET `TableAdapter` and `DataTable` constructs behave much like a Table Data Gateway, handing back loosely-typed rows for a whole table, while hand-rolled DAO layers pairing one gateway class per persistent domain object in Java systems of the same era resemble Row Data Gateway paired with a Domain Model. Recognizing which one a codebase has chosen — and why — is really about recognizing whether the surrounding code is procedural (Transaction Script) or object-oriented with real domain behavior (Domain Model), since that surrounding style is what each gateway pattern was designed to serve.
- If a domain object needs custom validation and business rules beyond plain CRUD, would you still use a pure Row Data Gateway to hold that logic?No — a pure Row Data Gateway by definition holds no business logic; you'd pair it with a separate Domain Model object that delegates persistence calls to the gateway, keeping data access and business rules in different classes. Putting the business rules directly on the gateway would effectively reinvent Active Record.
- Why is Table Data Gateway a natural fit for Transaction Script-style code?Transaction Script organizes logic as a set of procedures per business transaction rather than as behavior-rich objects, so it needs a simple, stateless way to run SQL per table; Table Data Gateway's single-instance, parameter-in/rows-out methods match that procedural style without requiring an object per row.
- What's a concrete performance risk of choosing Row Data Gateway for a report that scans 500,000 rows?Instantiating one gateway object per row means allocating hundreds of thousands of short-lived objects, which pressures the garbage collector and adds per-object overhead; a Table Data Gateway (or streaming a raw result set) avoids that by handing back lightweight records instead of wrapping every row.
Row Data Gateway is like having one librarian per book who only knows about that book; Table Data Gateway is one librarian for the whole shelf who looks up whichever book you ask for by call number.
saying these in an interview costs you the question
- Says Row Data Gateway and Active Record are the same pattern
- Can't explain why Table Data Gateway is usually a single/stateless instance
- Puts validation or business rules inside a 'pure' gateway class
- Doesn't recognize the memory implication of one object per row at scale
- Confuses Table Data Gateway with Table Module (a different PoEAA pattern)