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

Aspect
Active Record
Repository
Data Mapper

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?