Definition
The active record design pattern (considered an architectural pattern) is an approach that connects the application's business logic to a database table, allowing developers to work with the database using an object-oriented paradigm.
The active record is an object that wraps a row in the database table, encapsulates the database access and provides an interface to query, insert, update and delete data from the database, making it easier to work with databases in an application.
In addition to query, insert, update and delete functions, the interface on an object conforming to this pattern would also include properties that correspond more or less directly to the columns in the underlying database table.
After the creation of an object, a new row is added to the table upon saving. Any object loaded gets its information from the database. When an object is updated, the corresponding row in the table is also updated. The wrapper class implements accessor methods or properties for each column in the table or view.
The active record pattern is commonly used by many programming frameworks that store in-memory object data in relational databases, such as Django and Laravel.
Name origin
The pattern was named by Martin Fowler in his book Patterns of Enterprise Application Architecture (2003).
Implementation
This pattern is intended to make simple CRUD (Create, Read, Update, Delete) tasks quicker to achieve. For example, instead of writing a lot of SQL queries to insert or update many common and simple data objects, it allows us to simply assign values to the data object and run a command to save it, e.g. $object->save()
. The SQL for inserting/updating data will be compiled and executed for us.
For example, if there is a table users
in a database with columns name
(string type) and age
(number type), and the Active Record pattern is implemented in the class User
, the pseudo-code will create a new row in the users
table with the given values:
$user = new User();
$user->name = 'John Doe';
$user->age = 25;
$user->save();
The pseudo-code above is roughly equivalent to the SQL command:
INSERT INTO users (name, age) VALUES ('John Doe', 25);
Conversely, the class can be used to query the database:
$user = User::where('name', 'John Doe')->first();
This will find a new User
object based on the first matching row from the users
table whose name
column has the value "John Doe". The SQL command used might be similar to the following, depending on the SQL implementation details of the database:
SELECT * FROM users WHERE name = 'John Doe' LIMIT 1;
Most frameworks also implement data relationships within their respective Active Record models which can greatly simplify accessing data related to our object. For example, in Laravel, if we specified that a Category
"has many" Product
`s then after loading the Category
object from the database, we can list its child Product
`s with a simple line of code:
foreach ($category->products as $product) {
echo $product->name;
}
When to use it
Active Record is a good choice for domain logic that is not too complex, such as creates, reads, updates, and deletes. Derivations and validations based on a single record work well in this structure.
Criticism
Mapping issues
Active Record has the primary advantage of simplicity. It is easy to build Active Records, and they are easy to understand. Their primary problem is that they work well only if the Active Record objects correspond directly to the database tables: an isomorphic schema.
If your business logic is complex, you will soon want to use your object's direct relationships, collections, inheritance, and so forth. These do not map easily onto Active Record, and adding them piecemeal gets very messy. That is what will lead you to use Data Mapper instead.
Testability
Due to the coupling of database interaction and application logic when using the active record pattern, unit testing an active record object without a database becomes difficult. These negative effects on the testability of the active record pattern can be reduced by using mocking or dependency injection frameworks to substitute the real data tier with a simulated one.
Distributed systems
Record-based patterns work poorly in distributed systems, especially where concurrency is impossible (e.g. offline). i.e. two updates both may have one field that is correct but only one of the two records can win.
Single responsibility principle and separation of concerns
Due to the strong coupling of database interaction and application logic, an active record object does not follow the single responsibility principle and separation of concerns as opposed to multitier architecture which properly addresses these practices.
Conclusion
Active Record is good for quick CRUD-based applications where the Model is relatively flat (as in, not a lot of class hierarchies). However, for applications with complex OO hierarchies, a DataMapper is probably a better solution.
I hope you found this information helpful, stay tuned for more content! :)