Design Patterns
Choose the right architectural approach - Master Active Record, Repository, and Data Mapper patterns for scalable Crystal applications
Design patterns are proven solutions to recurring problems in software architecture. In the world of Object-Relational Mapping (ORM) and database interactions, choosing the right pattern can significantly impact your application's maintainability, testability, and scalability.
CQL supports multiple design patterns, giving you the flexibility to choose the approach that best fits your application's needs and complexity. This guide explores the key data access patterns available in CQL and helps you make informed architectural decisions.
π Table of Contents
π― Pattern Overview
CQL supports three main architectural patterns for data access, each with distinct characteristics and use cases:
ποΈ Active Record Pattern
"An object that wraps a row in a database table"
Models contain both data and behavior
Database operations are methods on the model
Ideal for rapid development and simple domains
π¦ Repository Pattern
"A collection-like interface for accessing domain objects"
Separates data access logic from business logic
Provides a uniform interface for data operations
Excellent for testing and complex queries
π Data Mapper Pattern
"A layer that moves data between objects and database"
Complete separation between domain and persistence
Maximum flexibility for complex domain models
Ideal for sophisticated business logic
π Pattern Comparison
Complexity
π’ Low
π‘ Medium
π΄ High
Learning Curve
π’ Easy
π‘ Moderate
π΄ Steep
Development Speed
π’ Fast
π‘ Medium
π΄ Slower
Testability
π‘ Good
π’ Excellent
π’ Excellent
Flexibility
π‘ Limited
π’ High
π’ Maximum
Separation of Concerns
π΄ Poor
π’ Good
π’ Excellent
Best for
CRUD apps
Data-centric
Complex domains
π― When to Use Each Pattern
Choose Active Record when:
Building CRUD-heavy applications
Working with simple domain models
Prioritizing rapid development
Team is new to ORMs
Requirements are stable
Choose Repository when:
Need clear separation of concerns
Building data-centric applications
Complex querying requirements
High testability requirements
Multiple data sources
Choose Data Mapper when:
Complex business domain
Rich domain models with sophisticated logic
Need complete persistence ignorance
Working with legacy databases
Maximum flexibility required
ποΈ Active Record Pattern
"An object that wraps a row in a database table, encapsulates database access, and adds domain logic on that data" - Martin Fowler
The Active Record pattern combines data and behavior in a single object, making database records behave like objects with both data and methods that operate on that data.
π― Key Characteristics
Data + Behavior: Models contain both properties and business logic
Direct Database Access: Objects can save, update, and delete themselves
Inheritance-based: Models inherit persistence capabilities
Convention over Configuration: Sensible defaults reduce boilerplate
π Implementation in CQL
β
Advantages
Rapid Development: Quick to implement and understand
Intuitive API: Natural object-oriented interface
Rich Query Interface: Built-in querying capabilities
Automatic Persistence: Objects know how to save themselves
Convention-based: Minimal configuration required
β Disadvantages
Tight Coupling: Domain logic tied to persistence layer
Limited Testability: Harder to mock database operations
Inheritance Constraints: Models must inherit from base class
Database Leakage: Database concerns can leak into domain logic
π― Best Use Cases
CRUD Applications: Admin panels, content management systems
Rapid Prototyping: Quick MVPs and proof-of-concepts
Simple Domains: Straightforward business logic
Small Teams: Easy for new developers to understand
π Learn Active Record in Detail β
π¦ Repository Pattern
"Encapsulates the logic needed to access data sources, centralizing common data access functionality" - Microsoft
The Repository pattern provides a uniform interface for accessing data, regardless of the underlying storage mechanism. It acts as a collection of domain objects in memory.
π― Key Characteristics
Collection Interface: Treat database like an in-memory collection
Abstraction Layer: Hide persistence details from domain logic
Centralized Queries: All data access logic in one place
Testability: Easy to mock for unit testing
π Implementation in CQL
β
Advantages
Separation of Concerns: Clean boundary between domain and data access
Testability: Easy to mock repositories for unit testing
Flexibility: Can swap data sources without changing domain logic
Centralized Queries: All data access logic in one place
Collection Metaphor: Intuitive interface for domain objects
β Disadvantages
More Code: Requires additional repository classes
Mapping Overhead: Manual mapping between database and domain objects
Learning Curve: More complex than Active Record
Potential Duplication: May duplicate some CRUD logic
π― Best Use Cases
Complex Querying: Applications with sophisticated data access patterns
High Testability: When unit testing is critical
Multiple Data Sources: When you need to abstract data access
Team Collaboration: Clear boundaries for different team members
π§ͺ Testing with Repository Pattern
π Learn Repository Pattern in Detail β
π Data Mapper Pattern
"A layer of software that separates the in-memory objects from the database" - Martin Fowler
The Data Mapper pattern provides complete separation between domain objects and database persistence, allowing domain objects to be completely ignorant of the database.
π― Key Characteristics
Complete Separation: Domain objects know nothing about persistence
Mapper Layer: Dedicated classes handle object-relational mapping
Persistence Ignorance: Domain objects focus purely on business logic
Maximum Flexibility: Can map any object structure to any database schema
π Implementation Concept in CQL
β
Advantages
Complete Separation: Domain objects are completely ignorant of persistence
Rich Domain Models: Full focus on business logic and behavior
Flexibility: Can map any object structure to any database schema
Testability: Domain objects are pure and easy to test
Legacy Integration: Can work with existing database schemas
β Disadvantages
Complexity: Requires significant additional code and abstractions
Learning Curve: Most complex pattern to understand and implement
Performance: Additional mapping layer can impact performance
Development Time: Slower initial development compared to other patterns
π― Best Use Cases
Complex Domain Logic: Rich business rules and sophisticated models
Legacy Database Integration: Working with existing, non-optimal schemas
Domain-Driven Design: When following DDD principles strictly
Large Teams: Clear separation enables parallel development
Long-term Projects: Investment in flexibility pays off over time
π Learn Data Mapper Concepts β
π¨ Pattern Selection Guide
π€ Decision Matrix
π Selection Checklist
Choose Active Record if:
Choose Repository if:
Choose Data Mapper if:
π― Hybrid Approaches
You don't have to choose just one pattern! CQL allows mixing patterns within the same application:
π Migration Strategies
ποΈ From Active Record to Repository
π¦ From Repository to Data Mapper
π‘ Best Practices
π― General Guidelines
Start Simple, Evolve as Needed
Use Consistent Patterns Within Bounded Contexts
Optimize for Your Team's Skills
π§ͺ Testing Strategies
Active Record Testing
Repository Testing
Data Mapper Testing
π§ Performance Considerations
Active Record Optimization
Repository Optimization
π Summary
Choosing the right data access pattern is crucial for building maintainable Crystal applications. Each pattern offers different trade-offs:
ποΈ Active Record: Perfect for rapid development and simple domains
π¦ Repository: Ideal for testability and separation of concerns
π Data Mapper: Best for complex domains and maximum flexibility
Remember that you can mix patterns within the same application, and you can always evolve from simpler to more sophisticated patterns as your application grows.
The key is to start simple and refactor when complexity demands it. CQL's flexible architecture supports all these patterns, giving you the freedom to choose what works best for your specific needs.
π Ready to implement? Choose your pattern and dive into the detailed guides for implementation examples and best practices!
Next Steps:
Last updated
Was this helpful?