Browse pages
LS1003: Aggregate Has No State Record
LS1003 is a compiler check that reports an aggregate with no State record, where an aggregate is the type that owns a unit of business state that changes in one step. Loose lifecycle flags let callers combine states the business never allows. This page states what the rule reports, where the warning sits, and how to give the aggregate a State record.
Reference#
What the rule enforces#
LS1003 enforces Model every Aggregate lifecycle with state records (litespec/backend/dotnet/domain#model-every-aggregate-lifecycle-with-state-records). It runs in Domain only. Its category is LiteSpec.Domain. It reports a warning and is enabled by default, as the Analyzers overview describes. It decides at the end of compilation, after every type is known.
What the rule reports#
The rule reports an aggregate class, recognized by a base class named AggregateRoot with one type argument, that exposes no State property typed as an abstract record. The rule walks the inheritance chain until the AggregateRoot base, so an inherited State property counts. The diagnostic sits on the aggregate name.
Failing example#
public sealed class Order : AggregateRoot<OrderId>
{
public Order(OrderId id) : base(id) { }
}Passing example#
public sealed class Order : AggregateRoot<OrderId>
{
public Order(OrderId id, OrderState state) : base(id) { State = state; }
public OrderState State { get; }
}
public abstract record OrderState;
public sealed record OrderDraftState : OrderState;
public sealed record OrderPlacedState(DateTimeOffset PlacedAt) : OrderState;How to fix#
Add a State property typed as an abstract {Aggregate}State record and derive one sealed record per lifecycle state from it.
Notes#
What the rule ignores#
The rule does not report classes that are not aggregates, or aggregates whose State property is an abstract record. It does not run outside Domain.