Table of Contents

Class FakeRepository<TAggregate, TId>

Namespace
Trellis.Testing
Assembly
Trellis.Testing.dll

In-memory fake repository for testing aggregates. Provides a simple in-memory store with domain event tracking.

public class FakeRepository<TAggregate, TId> where TAggregate : Aggregate<TId> where TId : notnull

Type Parameters

TAggregate

The aggregate type.

TId

The aggregate ID type.

Inheritance
FakeRepository<TAggregate, TId>
Inherited Members
Extension Methods

Remarks

Two persistence surfaces are exposed and they are intentional opposites:

See cookbook Recipe 16 — Unit of work in handlers: Add staging vs immediate SaveAsync for the full guidance on which method to call from where.

Properties

Count

Gets the count of stored aggregates.

public int Count { get; }

Property Value

int

PublishedEvents

Gets the list of domain events published by saved aggregates.

public IReadOnlyList<IDomainEvent> PublishedEvents { get; }

Property Value

IReadOnlyList<IDomainEvent>

Methods

Add(TAggregate)

Stages a new aggregate for insertion into the in-memory store, mirroring RepositoryBase<TAggregate, TId>.Add. Use this from handlers (and from test setup that exercises handlers) so the same void Add(T) surface works in both the EF and fake implementations of an IRepository contract.

public void Add(TAggregate aggregate)

Parameters

aggregate TAggregate

The aggregate to stage.

Remarks

The fake has no separate commit boundary — Add immediately makes the aggregate visible to subsequent FindByIdAsync/WhereAsync calls and captures its uncommitted domain events into PublishedEvents.

Unique constraints registered with WithUniqueConstraint(Func<TAggregate, object?>) are enforced eagerly here: a violation throws InvalidOperationException rather than returning Error.Conflict. The reason is that Add is a setup affordance — failures usually mean the test itself is wrong, and a loud throw points to the offending call site immediately. To test production conflict handling, call SaveAsync(TAggregate, CancellationToken) instead and assert on the returned Result.

Clear()

Clears all stored aggregates and published events.

public void Clear()

DeleteAsync(TId, CancellationToken)

Deletes an aggregate by its ID.

public Task<Result<Unit>> DeleteAsync(TId id, CancellationToken cancellationToken = default)

Parameters

id TId

The aggregate ID.

cancellationToken CancellationToken

Cancellation token.

Returns

Task<Result<Unit>>

A Result<TValue> with Unit indicating success or Error.NotFound.

Exists(TId)

Checks if an aggregate with the specified ID exists.

public bool Exists(TId id)

Parameters

id TId

The aggregate ID.

Returns

bool

True if the aggregate exists, false otherwise.

FindAsync(Func<TAggregate, bool>)

Finds the first aggregate matching the predicate, returning None if no match. Use in test repository adapters for custom query methods (e.g., FindByEmailAsync).

public Task<Maybe<TAggregate>> FindAsync(Func<TAggregate, bool> predicate)

Parameters

predicate Func<TAggregate, bool>

The predicate to match.

Returns

Task<Maybe<TAggregate>>

Maybe with the first matching aggregate or None.

FindByIdAsync(TId, CancellationToken)

Finds an aggregate by its ID, returning Maybe if not found.

public Task<Maybe<TAggregate>> FindByIdAsync(TId id, CancellationToken cancellationToken = default)

Parameters

id TId

The aggregate ID.

cancellationToken CancellationToken

Cancellation token.

Returns

Task<Maybe<TAggregate>>

Maybe with the aggregate or None.

Get(TId)

Gets an aggregate by ID without wrapping in Result.

public TAggregate? Get(TId id)

Parameters

id TId

The aggregate ID.

Returns

TAggregate

The aggregate or null if not found.

GetAll()

Gets all stored aggregates.

public IEnumerable<TAggregate> GetAll()

Returns

IEnumerable<TAggregate>

All aggregates in the repository.

GetByIdAsync(TId, CancellationToken)

Gets an aggregate by its ID.

public Task<Result<TAggregate>> GetByIdAsync(TId id, CancellationToken cancellationToken = default)

Parameters

id TId

The aggregate ID.

cancellationToken CancellationToken

Cancellation token.

Returns

Task<Result<TAggregate>>

A Result containing the aggregate or an Error.NotFound.

Remove(TAggregate)

Stages an aggregate for deletion from the in-memory store, mirroring RepositoryBase<TAggregate, TId>.Remove. No-op if the aggregate is not in the store (matching the EF semantics where the change tracker accepts the call without verifying database existence).

public void Remove(TAggregate aggregate)

Parameters

aggregate TAggregate

The aggregate to remove.

RemoveByIdAsync(TId, CancellationToken)

Looks up an aggregate by ID and removes it. Returns Error.NotFound if the aggregate does not exist. Mirrors RepositoryBase<TAggregate, TId>.RemoveByIdAsync.

public Task<Result<Unit>> RemoveByIdAsync(TId id, CancellationToken cancellationToken = default)

Parameters

id TId

The aggregate ID to remove.

cancellationToken CancellationToken

Cancellation token (ignored — operation is synchronous).

Returns

Task<Result<Unit>>

A Result<TValue> with Unit indicating success or not-found failure.

SaveAsync(TAggregate, CancellationToken)

Saves an aggregate and captures its domain events.

public Task<Result<Unit>> SaveAsync(TAggregate aggregate, CancellationToken cancellationToken = default)

Parameters

aggregate TAggregate

The aggregate to save.

cancellationToken CancellationToken

Cancellation token.

Returns

Task<Result<Unit>>

A Result<TValue> with Unit indicating success or failure.

WhereAsync(Func<TAggregate, bool>)

Returns all aggregates matching the predicate. Use in test repository adapters for custom query methods (e.g., GetByCustomerIdAsync).

public Task<IReadOnlyList<TAggregate>> WhereAsync(Func<TAggregate, bool> predicate)

Parameters

predicate Func<TAggregate, bool>

The predicate to filter by.

Returns

Task<IReadOnlyList<TAggregate>>

A list of matching aggregates.

WhereAsync(Specification<TAggregate>)

Returns all aggregates matching the specification. Use in test repository adapters for specification-based queries (e.g., GetOverdueOrdersAsync).

public Task<IReadOnlyList<TAggregate>> WhereAsync(Specification<TAggregate> specification)

Parameters

specification Specification<TAggregate>

The specification to evaluate.

Returns

Task<IReadOnlyList<TAggregate>>

A list of matching aggregates.

WithUniqueConstraint(Func<TAggregate, object?>)

Adds a unique constraint on the specified property. When SaveAsync(TAggregate, CancellationToken) is called, the repository checks that no other aggregate (with a different ID) has the same value for this property. Returns an Error.Conflict on violation.

public FakeRepository<TAggregate, TId> WithUniqueConstraint(Func<TAggregate, object?> propertySelector)

Parameters

propertySelector Func<TAggregate, object>

A function selecting the property to constrain.

Returns

FakeRepository<TAggregate, TId>

This repository for fluent chaining.

Examples

var repo = new FakeRepository<Customer, CustomerId>()
    .WithUniqueConstraint(c => c.Email);