Skip to content

Repositories

Info

At ConnectSoft, Repositories are the gateways between the pure domain model
and the persistence infrastructure — enabling clean architecture, testability, and resilient domain evolution.


Introduction

In Domain-Driven Design (DDD), a Repository acts as a collection-like abstraction
for accessing and persisting Aggregates and Entities.

Repositories:

  • Hide infrastructure concerns (databases, NoSQL, files, APIs) from the Domain Layer.
  • Provide explicit access patterns tailored to Aggregates.
  • Enable testability by allowing domain logic to operate without infrastructure coupling.
  • Support transactionally consistent operations when needed.

At ConnectSoft, designing strong, tactical Repositories is critical for:

  • Microservices resilience
  • Event-sourced workflows
  • CQRS separation
  • AI and SaaS system evolution

Concept Definition

A Repository:

Aspect Description
Purpose Abstracts Aggregate persistence and retrieval.
Ownership Each Aggregate typically has its own Repository.
Access Mode Operates in terms of Aggregates (not raw tables, DTOs, or documents).
Boundary Lives at the Application Layer boundary — interfaces defined toward Domain Layer, implementations in Infrastructure Layer.
Lifecycle Support Supports add, update, delete, and query operations based on domain needs.

📚 Why Repositories Matter at ConnectSoft

Decouple Domain from Infrastructure

  • Domain logic remains clean — it never talks to databases directly.

Enable Persistence Agility

  • Databases (SQL, NoSQL, Event Store) can change without modifying domain logic.

Simplify Testing

  • Mock Repositories allow pure domain behavior testing without real database dependencies.

Support Consistency Boundaries

  • Repository transactions enforce aggregate consistency across modifications.

Encourage Tactical Design

  • Each Repository models exactly what the Aggregate needs — no leaky abstractions or generic "save everything" patterns.

Repository vs DAO (Data Access Object)

Aspect Repository DAO
Scope Aggregate-oriented Table or record-oriented
Design Level Tactical (business abstraction) Technical (storage abstraction)
Clients Domain/Use Cases Infrastructure services
Operations Business-driven (e.g., FindActiveOrder) CRUD-driven (select, update, delete)
Focus Language of the domain Language of the database

🧩 Visual: Repository in System Layers

flowchart TD
    UI["UI/API Layer"]
    AppService["Application Service"]
    Repository["Repository (Aggregate Gateway)"]
    Database["Database / Storage Layer"]

    UI --> AppService
    AppService --> Repository
    Repository --> Database
Hold "Alt" / "Option" to enable pan & zoom

Application Services use Repositories to interact with Aggregates.
Domain Layer remains isolated from database or infrastructure concerns.


Strategic Design Principles for Repositories

At ConnectSoft, we model Repositories to protect the domain model,
support flexible persistence evolution, and enable testable, tactical architectures.


📚 Core Principles for Repository Design

Aggregate-Centric Access

  • Repositories expose operations in terms of Aggregates, not database tables or technical schemas.

Keep Interfaces Small and Focused

  • Expose only methods actually needed by the domain use cases (e.g., GetActiveSubscriptionByUserId()), not general-purpose CRUD.

Isolate Persistence Infrastructure

  • Domain and Application Layers never depend on ORM models, SQL, or NoSQL documents.

Support Transaction Consistency for Aggregates

  • Changes across one Aggregate must be transactionally consistent — enforced via the Repository + Unit of Work.

Enable Testability

  • Repositories should allow in-memory, mock, or fake implementations for easy testing.

Return Rich Domain Models, Not Anemic DTOs

  • Always return fully loaded Aggregates or Entities, ready for domain behavior execution.

Support Optimistic Concurrency Where Needed

  • For distributed systems, implement versioning to avoid lost updates.

📚 Repository Interface Example (Tactical)

public interface IOrderRepository
{
    Task<Order> GetByIdAsync(Guid orderId);
    Task<IEnumerable<Order>> GetPendingOrdersAsync();
    Task AddAsync(Order order);
    Task UpdateAsync(Order order);
    Task DeleteAsync(Order order);
}

✅ Operates purely in terms of the Order Aggregate.

✅ No leaking of database-specific details.


Transaction Handling in Repositories

✅ At ConnectSoft, transactional consistency is critical when modifying Aggregates.

Context Guideline
Single Aggregate Changes Repository methods save or update one Aggregate per transaction.
Multiple Aggregates Use Application Services + Domain Services to coordinate, but respect one Repository per Aggregate boundary.
Unit of Work Pattern Aggregate all Repository changes into a single transaction boundary at Application Service level when needed.
Outbox Pattern for Events Persist domain events inside the same transaction if events are raised by Aggregates.

🛑 Common Anti-Patterns to Avoid with Repositories

Anti-Pattern Symptom Why It's Dangerous
Generic CRUD Repositories IGenericRepository<TEntity> everywhere. Forces leaky abstractions, bloats services with meaningless operations.
Repository Does Too Much Repository manages multiple aggregates, reads, writes, queries, events. Violates SRP (Single Responsibility Principle), impossible to test properly.
Direct Database Exposure Application Service or Domain talks directly to DbContext/SQL. Breaks layering, hurts domain isolation, kills testability.
Entity Framework First Design Repository models follow database shape. Breaks DDD language, domain model purity collapses.
No Unit of Work When Needed Each Repository call runs in isolation. Causes transaction consistency issues when multiple changes must happen together.

📚 Good vs Bad Repository Patterns

✅ Good Repository 🚫 Bad Repository
Aggregate-focused methods Generic Save/Delete without context
Operates in Domain language Operates in SQL/table language
Supports clear transaction boundaries Each method opens/closes separate transactions
Returns Aggregates/Entities Returns raw persistence models

🧩 Visual: Repository + Unit of Work Transaction Flow

sequenceDiagram
    participant AppService as Application Service
    participant OrderRepo as Order Repository
    participant PaymentRepo as Payment Repository
    participant UnitOfWork as Unit of Work
    participant Database

    AppService->>UnitOfWork: Begin Transaction
    AppService->>OrderRepo: Add New Order
    AppService->>PaymentRepo: Add Payment Record
    UnitOfWork->>Database: Commit Transaction
Hold "Alt" / "Option" to enable pan & zoom

✅ Multiple Repositories work inside one Unit of Work to guarantee transaction consistency.


ConnectSoft.Extensions.PersistenceModel Repository Infrastructure

At ConnectSoft, we use the ConnectSoft.Extensions.PersistenceModel package to provide a consistent, powerful foundation for all repository implementations.

Base Interfaces and Classes:

  • IGenericRepository<TEntity, TIdentity> — Base interface providing standard CRUD operations
  • GenericRepository<TEntity, TIdentity> — Abstract base class for NHibernate-based repositories
  • MongoDbRepository<TEntity, TIdentity> — Base class for MongoDB-based repositories
  • IUnitOfWork — Unit of Work pattern for transactional consistency
  • ISpecification<TEntity, TIdentity> — Specification pattern for complex queries

Key Characteristics:

  • Aggregate-Centric — Repositories operate on aggregate roots (IAggregateRoot<TIdentity>)
  • Unit of Work Integration — All operations work within transaction boundaries
  • Specification Pattern — Supports fluent query building without bloating repository API
  • Async/Await Support — Both synchronous and asynchronous methods available
  • Multiple Persistence Models — Supports NHibernate, MongoDB, and more
  • Keyed Dependency Injection — Supports multiple persistence models in the same microservice

C# Examples: Real-World Repository Modeling at ConnectSoft

Repositories are modeled tactically per Aggregate,
supporting domain workflows, transactional safety, and future evolvability.

All examples below are based on real implementations from the ConnectSoft.Saas.ProductsCatalog microservice.


🛠️ Real-World Example: ProductRepository from ConnectSoft.Saas.ProductsCatalog

Step 1: Define the Repository Interface

Real Implementation from ConnectSoft.Saas.ProductsCatalog.PersistenceModel:

namespace ConnectSoft.Saas.ProductsCatalog.PersistenceModel.Repositories
{
    using System;
    using ConnectSoft.Extensions.PersistenceModel.Repositories;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel;

    /// <summary>
    /// Mediates between the domain and data mapping layers
    /// using a collection-like interface for accessing
    /// Products domain objects.
    /// </summary>
    public interface IProductsRepository : IGenericRepository<IProduct, Guid>
    {
    }
}

Key Pattern Elements:

Extends IGenericRepository<TEntity, TIdentity> — Inherits standard CRUD operations
Aggregate Root Type — Operates on IProduct which extends IAggregateRoot<Guid>
Minimal Interface — No custom methods needed if standard operations suffice
Domain-Focused — Interface is in PersistenceModel layer, not infrastructure

Standard Operations from IGenericRepository<TEntity, TIdentity>:

  • GetById(TIdentity id) / GetByIdAsync(TIdentity id) — Retrieve by identifier
  • GetAll(bool cacheable = false) / GetAllAsync(bool cacheable = false) — Retrieve all entities
  • Insert(TEntity entity) / InsertAsync(TEntity entity) — Add new entity
  • Update(TEntity entity) / UpdateAsync(TEntity entity) — Update existing entity
  • Delete(TEntity entity) / DeleteAsync(TEntity entity) — Delete entity
  • Delete(TIdentity id) / DeleteAsync(TIdentity id) — Delete by identifier
  • Query(Expression<Func<TEntity, bool>> filter) — Query with LINQ expressions
  • Specify<TSpecification>() — Fluent specification builder

Step 2: Implement NHibernate Repository

Real Implementation from ConnectSoft.Saas.ProductsCatalog.PersistenceModel.NHibernate:

namespace ConnectSoft.Saas.ProductsCatalog.PersistenceModel.NHibernate.Repositories
{
    using System;
    using ConnectSoft.Extensions.PersistenceModel;
    using ConnectSoft.Extensions.PersistenceModel.Repositories;
    using ConnectSoft.Extensions.PersistenceModel.Specifications;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel;
    using ConnectSoft.Saas.ProductsCatalog.PersistenceModel.Repositories;

    /// <summary>
    /// Generic repository implementation that provides unified access
    /// to the Products entities stored in underlying data storage.
    /// </summary>
    /// <remarks>
    /// Initializes a new instance of the <see cref="ProductsRepository" /> class
    /// with a specified dependencies.
    /// </remarks>
    /// <param name="unitOfWork">Unit of work instance.</param>
    /// <param name="specificationLocator">Specification locator instance.</param>
    public class ProductsRepository(IUnitOfWork unitOfWork, ISpecificationLocator specificationLocator)
        : GenericRepository<IProduct, Guid>(unitOfWork, specificationLocator), IProductsRepository
    {
    }
}

Key Implementation Patterns:

Inherits from GenericRepository<IProduct, Guid> — Base class handles all CRUD operations
Implements IProductsRepository — Satisfies the repository contract
Unit of Work Dependency — All operations work within transaction boundaries
Specification Locator — Enables specification pattern for complex queries
Minimal Implementation — No code needed if base class provides all required functionality


Step 3: Implement MongoDB Repository (Alternative Persistence)

Real Implementation showing MongoDB persistence option:

// Ignore Spelling: Mongo

namespace ConnectSoft.Saas.ProductsCatalog.PersistenceModel.MongoDb.Repositories
{
    using System;
    using ConnectSoft.Extensions.PersistenceModel;
    using ConnectSoft.Extensions.PersistenceModel.MongoDb.Repositories;
    using ConnectSoft.Extensions.PersistenceModel.Specifications;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel;
    using ConnectSoft.Saas.ProductsCatalog.PersistenceModel.Repositories;
    using MongoDB.Driver;

    /// <summary>
    /// MongoDb repository implementation that provides unified access
    /// to the Products entities stored in underlying data storage.
    /// </summary>
    /// <remarks>
    /// Initializes a new instance of the <see cref="ProductsMongoDbRepository"/> class.
    /// </remarks>
    /// <param name="unitOfWork">unit of work instance.</param>
    /// <param name="mongoDatabase">the mongoDb database.</param>
    /// <param name="specificationLocator">the specification locator.</param>
    public class ProductsMongoDbRepository(IUnitOfWork unitOfWork, IMongoDatabase mongoDatabase, ISpecificationLocator specificationLocator)
        : MongoDbRepository<IProduct, Guid>(unitOfWork, mongoDatabase, specificationLocator), IProductsRepository
    {
    }
}

Same Interface, Different ImplementationIProductsRepository can be backed by NHibernate or MongoDB
Infrastructure Agnostic — Application Services don't know which persistence technology is used
Flexible Deployment — Can switch persistence models without changing domain or application code


Step 4: Using Repository in Application Services

Real Implementation from ConnectSoft.Saas.ProductsCatalog.DomainModel.Impl:

namespace ConnectSoft.Saas.ProductsCatalog.DomainModel.Impl
{
    using System;
    using System.Threading.Tasks;
    using ConnectSoft.Extensions.PersistenceModel;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel.PocoEntities;
    using ConnectSoft.Saas.ProductsCatalog.PersistenceModel.Repositories;

    /// <summary>
    /// Default IProductsProcessor implementation to process, manage and store Products.
    /// </summary>
    public class DefaultProductsProcessor : IProductsProcessor
    {
        private readonly IProductsRepository repository;
        private readonly IUnitOfWork unitOfWork;

        // ... constructor ...

        /// <summary>
        /// Creates a new Product aggregate root.
        /// </summary>
        public async Task<IProduct> CreateProduct(CreateProductInput input, CancellationToken token = default)
        {
            // 1. Check if product already exists
            IProduct existingProduct = await this.repository.GetByIdAsync(input.ProductId);
            if (existingProduct != null)
            {
                throw new ProductAlreadyExistsException(input.ProductId);
            }

            // 2. Create new aggregate root
            ProductEntity newProduct = new ProductEntity()
            {
                ProductId = input.ProductId,
                CreationDate = this.dateTimeProvider.GetUtcNow().DateTime,
                Name = "Salesforce CRM Cloud",
                Status = ProductStatusEnumeration.Active,
                // ... other properties ...
            };

            // 3. Persist within transaction
            this.unitOfWork.ExecuteTransactional(() =>
            {
                this.repository.Insert(newProduct);  // ✅ Using repository
            });

            return newProduct;
        }

        /// <summary>
        /// Deletes a Product aggregate root.
        /// </summary>
        public async Task DeleteProduct(DeleteProductInput input, CancellationToken token = default)
        {
            // 1. Load aggregate root
            IProduct productToDelete = await this.repository.GetByIdAsync(input.ProductId);
            if (productToDelete == null)
            {
                throw new ProductNotFoundException(input.ProductId);
            }

            // 2. Delete within transaction
            this.unitOfWork.ExecuteTransactional(() =>
            {
                this.repository.Delete(productToDelete);  // ✅ Using repository
            });
        }
    }
}

Key Application Service Patterns:

Repository Injection — Repository is injected via constructor
Unit of Work IntegrationExecuteTransactional() ensures atomic operations
Async/Await — All repository operations use async methods
Null Handling — Repository methods return null if entity not found
Transaction Boundaries — All persistence operations wrapped in transactions


Step 5: Using Specification Pattern for Complex Queries

Real Implementation showing how to use specifications:

// Ignore Spelling: Queryable

namespace ConnectSoft.Saas.ProductsCatalog.PersistenceModel.NHibernate.Specifications
{
    using System;
    using System.Linq;
    using ConnectSoft.Extensions.PersistenceModel;
    using ConnectSoft.Extensions.PersistenceModel.Specifications.Queryable;
    using ConnectSoft.Saas.ProductsCatalog.EntityModel;
    using ConnectSoft.Saas.ProductsCatalog.PersistenceModel.Specifications;

    /// <summary>
    /// IQueryable based specification to search Products entities.
    /// </summary>
    public class ProductsQueryableSpecification(IUnitOfWorkConvertor unitOfWorkConvertor)
        : QueryableSpecification<IProduct, Guid>(unitOfWorkConvertor), IProductsSpecification
    {
        // Custom query methods can be added here
        // Example: FindActiveProducts(), FindByCategory(string category), etc.
    }
}

Usage in Application Service:

// Using Specification pattern for complex queries
var specification = repository.Specify<ProductsQueryableSpecification>();
var activeProducts = specification
    .Where(p => p.Status == ProductStatusEnumeration.Active)
    .ToList();

// Or using Query method with LINQ expressions
var freemiumProducts = repository.Query(p => 
    p.Status == ProductStatusEnumeration.Active &&
    p.PricingModel.PricingType == PricingTypeEnumeration.Freemium
);

Fluent Query Building — Specifications enable composable queries
Type Safety — Compile-time checking of query expressions
Separation of Concerns — Complex queries isolated in specification classes


📚 Key Lessons from ConnectSoft Repository Examples

Repositories inherit from IGenericRepository<TEntity, TIdentity> and implement aggregate-specific interfaces

Implementation classes inherit from GenericRepository<TEntity, TIdentity> (NHibernate) or MongoDbRepository<TEntity, TIdentity> (MongoDB)

Unit of Work pattern ensures transactional consistency

Specification pattern enables complex queries without bloating repository API

Multiple persistence models (NHibernate, MongoDB) can implement the same repository interface

Repository interfaces are in PersistenceModel layer, implementations in infrastructure-specific projects

Application Services use repositories through interfaces, remaining infrastructure-agnostic


📋 Complete Implementation Template

Here's a complete template you can use to create new repositories following ConnectSoft patterns:

namespace YourNamespace.PersistenceModel.Repositories
{
    using System;
    using ConnectSoft.Extensions.PersistenceModel.Repositories;
    using YourNamespace.EntityModel;

    /// <summary>
    /// Mediates between the domain and data mapping layers
    /// using a collection-like interface for accessing
    /// YourAggregateRoot domain objects.
    /// </summary>
    public interface IYourAggregateRootRepository : IGenericRepository<IYourAggregateRoot, Guid>
    {
        // Add custom methods here if needed (e.g., FindActiveItems(), FindByCategory())
    }
}
namespace YourNamespace.PersistenceModel.NHibernate.Repositories
{
    using System;
    using ConnectSoft.Extensions.PersistenceModel;
    using ConnectSoft.Extensions.PersistenceModel.Repositories;
    using ConnectSoft.Extensions.PersistenceModel.Specifications;
    using YourNamespace.EntityModel;
    using YourNamespace.PersistenceModel.Repositories;

    /// <summary>
    /// Generic repository implementation that provides unified access
    /// to the YourAggregateRoot entities stored in underlying data storage.
    /// </summary>
    /// <param name="unitOfWork">Unit of work instance.</param>
    /// <param name="specificationLocator">Specification locator instance.</param>
    public class YourAggregateRootRepository(IUnitOfWork unitOfWork, ISpecificationLocator specificationLocator)
        : GenericRepository<IYourAggregateRoot, Guid>(unitOfWork, specificationLocator), IYourAggregateRootRepository
    {
        // Override base methods or add custom methods here if needed
    }
}

Follow this pattern for all new repositories to ensure consistency across ConnectSoft projects.


🛠️ Example 2: Finance — AccountRepository (Soft Deletion Handling)

public interface IAccountRepository
{
    Task<Account> GetByIdAsync(Guid accountId);
    Task<IEnumerable<Account>> GetActiveAccountsAsync();
    Task AddAsync(Account account);
    Task DeactivateAsync(Account account);
}

public class AccountRepository : IAccountRepository
{
    private readonly DbContext _context;

    public AccountRepository(DbContext context)
    {
        _context = context;
    }

    public async Task<Account> GetByIdAsync(Guid accountId)
    {
        return await _context.Set<Account>()
            .Where(a => a.IsActive)
            .SingleOrDefaultAsync(a => a.Id == accountId);
    }

    public async Task<IEnumerable<Account>> GetActiveAccountsAsync()
    {
        return await _context.Set<Account>()
            .Where(a => a.IsActive)
            .ToListAsync();
    }

    public async Task AddAsync(Account account)
    {
        await _context.Set<Account>().AddAsync(account);
    }

    public async Task DeactivateAsync(Account account)
    {
        account.Deactivate();
        _context.Set<Account>().Update(account);
    }
}

✅ Handles soft deletion (deactivation) properly.
✅ All queries automatically filter for active accounts.


🎯 ConnectSoft Repository Checklist

When implementing repositories at ConnectSoft, ensure:

Checklist Item Description Example
Extend IGenericRepository<TEntity, TIdentity> Use base interface for standard operations IGenericRepository<IProduct, Guid>
Create Aggregate-Specific Interface Define repository interface in PersistenceModel layer IProductsRepository : IGenericRepository<IProduct, Guid>
Inherit from Base Repository Class Use GenericRepository (NHibernate) or MongoDbRepository (MongoDB) GenericRepository<IProduct, Guid>
Inject Unit of Work All operations require IUnitOfWork for transactions Constructor: IUnitOfWork unitOfWork
Inject Specification Locator Enable specification pattern for complex queries Constructor: ISpecificationLocator specificationLocator
Use Async Methods Prefer async/await for all I/O operations GetByIdAsync(), InsertAsync()
Wrap in Transactions Use UnitOfWork.ExecuteTransactional() for atomic operations unitOfWork.ExecuteTransactional(() => { ... })
Handle Null Returns Repository methods return null if entity not found if (entity == null) throw new NotFoundException()
Support Specifications Use specification pattern for complex queries repository.Specify<TSpecification>()
Multiple Persistence Models Support NHibernate, MongoDB, or both in same microservice Use keyed dependency injection for multiple models

🛠️ Example 3: Healthcare — PatientRepository with Specification Pattern

public interface IPatientRepository
{
    Task<Patient> GetByIdAsync(Guid patientId);
    Task<IEnumerable<Patient>> FindAsync(ISpecification<Patient> specification);
    Task AddAsync(Patient patient);
}

public interface ISpecification<T>
{
    Expression<Func<T, bool>> ToExpression();
}

public class ActivePatientsSpecification : ISpecification<Patient>
{
    public Expression<Func<Patient, bool>> ToExpression()
    {
        return patient => patient.IsActive && !patient.IsArchived;
    }
}

public class PatientRepository : IPatientRepository
{
    private readonly DbContext _context;

    public PatientRepository(DbContext context)
    {
        _context = context;
    }

    public async Task<Patient> GetByIdAsync(Guid patientId)
    {
        return await _context.Set<Patient>().FindAsync(patientId);
    }

    public async Task<IEnumerable<Patient>> FindAsync(ISpecification<Patient> specification)
    {
        return await _context.Set<Patient>()
            .Where(specification.ToExpression())
            .ToListAsync();
    }

    public async Task AddAsync(Patient patient)
    {
        await _context.Set<Patient>().AddAsync(patient);
    }
}

✅ Supports flexible dynamic queries without bloating Repository API.
✅ Keeps Repository clean and extensible over time.


📚 Lessons from Advanced Repository Modeling

Good Practice Why It Matters
Aggregate-centered methods Keeps use cases simple and tactical.
Support for dynamic queries (Specification) Avoid endless method expansion.
Handle Soft Deletes internally Protects external callers from data inconsistencies.
Infrastructure isolation Domain models never leak infrastructure concerns.
Compose clean Unit of Work transactions Enables distributed consistency across Aggregates.

🧩 Visual: Repository Usage in an Application Service

sequenceDiagram
    participant AppService as Application Service
    participant OrderRepository
    participant OrderAggregate

    AppService->>OrderRepository: GetByIdAsync(orderId)
    OrderRepository-->>AppService: Order Aggregate
    AppService->>OrderAggregate: Execute Business Behavior (Place Order)
    AppService->>OrderRepository: UpdateAsync(Order Aggregate)
Hold "Alt" / "Option" to enable pan & zoom

✅ Application Services use Repositories tactically to orchestrate domain behavior safely.


Best Practices for Repositories

At ConnectSoft, Repositories are treated as critical tactical patterns
preserving domain purity, enabling evolution, and safeguarding transactional resilience across microservices and complex systems.


📚 Best Practices Checklist

Model Repositories Per Aggregate

  • Each Aggregate gets its own Repository abstraction.

Expose Aggregate-Specific Operations Only

  • No generic CRUD methods leaking everywhere.

Operate at the Domain Level

  • Expose methods and queries meaningful to the business, not the database.

Protect Transaction Consistency

  • Changes within a transaction stay isolated until committed via Unit of Work.

Enable In-Memory Testing Easily

  • Repositories should allow mocking/faking for clean, fast unit testing.

Support Dynamic Queries via Specifications

  • Avoid endless method expansions with Specification Pattern or Query Objects.

Handle Soft Deletes or Status Filtering Internally

  • Never expose inactive/archived entities unintentionally.

Infrastructure Stays Hidden

  • No exposure of database, NoSQL, or ORM concerns to Application or Domain Layers.

Design for Resilience

  • Apply patterns like retrying transient failures and outbox integration where necessary.

Conclusion

Repositories at ConnectSoft are not just technical patterns
they are strategic design artifacts that enable:

  • Domain isolation and protection
  • Evolution of storage technologies
  • Clear tactical orchestration inside Application Services
  • Scalable, resilient event-driven microservice workflows

When Repositories are poorly modeled:

  • Domain models rot into persistence-driven shapes.
  • Testing becomes fragile and slow.
  • Transactional consistency becomes unreliable.
  • Evolution toward event-driven or distributed systems becomes risky.

When modeled properly:

  • Repositories shield the domain.
  • Systems adapt faster to business change.
  • Architectural integrity is preserved for the long term.

At ConnectSoft, Repositories stand guard over the domain,
ensuring that every Aggregate can grow, evolve, and thrive
regardless of how the technology landscape shifts beneath it.

"Repositories are the gates;
Guard them well, and your domain will flourish.
"


References

  • Books and Literature

    • Eric Evans — Domain-Driven Design: Tackling Complexity in the Heart of Software
    • Vaughn Vernon — Implementing Domain-Driven Design
    • Jimmy Nilsson — Applying Domain-Driven Design and Patterns
  • Online Resources

  • ConnectSoft Packages and Code

    • ConnectSoft.Extensions.PersistenceModel — Repository infrastructure:
      • IGenericRepository<TEntity, TIdentity> — Base repository interface with standard CRUD operations
      • GenericRepository<TEntity, TIdentity> — Abstract base class for NHibernate repositories
      • IUnitOfWork — Unit of Work pattern for transactional consistency
      • ISpecification<TEntity, TIdentity> — Specification pattern for complex queries
      • Async and synchronous method support
    • ConnectSoft.Extensions.PersistenceModel.NHibernate — NHibernate-specific implementations:
      • GenericRepository<TEntity, TIdentity> — NHibernate repository base class
      • QueryableSpecification<TEntity, TIdentity> — LINQ-based specification support
    • ConnectSoft.Extensions.PersistenceModel.MongoDb — MongoDB-specific implementations:
      • MongoDbRepository<TEntity, TIdentity> — MongoDB repository base class
      • MongoDB-specific query and specification support
    • Real-World Examples — See ConnectSoft.Saas.ProductsCatalog.PersistenceModel for production implementations:
      • IProductsRepository / ProductsRepository — Complete repository example
      • ProductsMongoDbRepository — MongoDB repository example
      • ProductsQueryableSpecification — Specification pattern example
      • DefaultProductsProcessor — Application Service using repository
  • ConnectSoft Internal Standards

    • ConnectSoft Repository and Persistence Layer Guidelines
    • ConnectSoft Microservices Transaction Patterns
    • ConnectSoft Event-Driven Outbox Pattern Blueprints