Type to search pages and rules.

Browse pages

Layer Projects

This extension packages each backend layer as its own .NET project: Domain, Application.Abstractions, Application, Infrastructure, and WebApi, with hosts beside them. It states the five-project form of the project, reference, package, and layout rules for backends already on five projects and for teams that want the compiler to hold Domain purity. The module layout is the default; this page holds the packaging that replaces it.

Markdown14 provisions

Stack: dotnet Layer: application

Activation#

Activation scope: project.

Applicable specification kinds: None.

The consumer selects layer-projects when its backend already packages each layer as its own project, or when the team wants the compiler to hold Domain purity through project references. Without this extension, the module layout applies.

Baseline Relationship#

For a project selecting this extension, this extension replaces:

  • litespec/backend/architecture#organize-code-by-module-aggregate-and-use-case: one folder per module in every project replaces the one module tree.
  • litespec/backend/dotnet/architecture#separate-the-shared-kernel-from-the-modules: five layer projects replace the shared kernel and modules projects with their hosts.
  • litespec/backend/dotnet/architecture#keep-host-endpoints-in-hosts: host endpoints without a handler live in the WebApi project instead of beside the shared kernel and modules projects.
  • litespec/backend/dotnet/architecture#mirror-modules-aggregates-and-use-cases-in-one-namespace-tree: one folder and namespace per module in every project replaces the one namespace tree.
  • litespec/workspace/dependencies#follow-the-declared-dependency-graph: project references between the five projects replace references from the hosts to the shared kernel and modules projects.
  • litespec/workspace/dependencies#keep-domain-dependency-free: a Domain project with no package references replaces analyzer-checked purity of the Domain layer folders.
  • litespec/workspace/dependencies#keep-application-free-of-provider-packages: an Application project with narrow references replaces the same boundary on the Application layer folders.
  • litespec/workspace/dependencies#assign-each-baseline-package-to-its-owning-layer: package ownership by project replaces ownership by layer folder.
  • litespec/backend/dotnet/packages#run-litespec-analyzers-in-the-business-projects-and-hosts: the analyzers run in every layer project instead of in the shared kernel and modules projects and the hosts.
  • litespec/backend/dotnet/domain#use-the-domain-layer-folder-layout: aggregate folders in the Domain project replace aggregate layer folders in the module tree.
  • litespec/backend/dotnet/application#use-the-use-case-folder-layout: operation folders in the Application project replace operation files in the UseCases/ use case folders.
  • litespec/backend/dotnet/api#place-endpoints-in-their-use-case-folders: endpoint folders in the WebApi project replace endpoint files in the use case folders.
  • litespec/backend/dotnet/persistence#use-the-persistence-layer-folder-layout: the Infrastructure persistence folder replaces the shared kernel and module Infrastructure folders.
  • litespec/backend/testing#keep-the-test-suites-the-size-requires: Domain.Tests and Application.Tests beside Architecture, Integration, and Acceptance replace the one unit test project beside them.
  • litespec/workspace/structure#keep-production-and-test-code-separate: the five-project source tree replaces the shared kernel and modules projects beside their hosts.

Every other baseline provision binds unchanged.

This extension switches on no optional record type.

Standards#

Keep One Application Assembly#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#keep-one-application-assembly
Statement

An application MUST place commands, queries, results, validators, handlers, event handler classes, and module-owned ports in one Application project, and cross-module contracts in Application.Abstractions, or in Application where the project merges them under litespec/backend/dotnet/ext/layer-projects#use-five-application-projects.

Why

Splitting Application into Write, Read, Contracts, and event-handler assemblies would buy a deployment boundary the stack never uses, and every use case would pay for it with a project reference to cross. The separation this stack keeps is the one Fowler describes as CQRS: the command and query models differ, while one assembly holds them. Event handlers live in Application On{Fact} folders of the aggregate whose records they change; job host classes live in Worker or Infrastructure with the scheduling concerns, under litespec/backend/architecture#declare-the-execution-host-for-work-that-outlives-a-request.

Checka reviewer answers

Reviewed forWhich command, query, result, validator, handler, or event handler files escape the Application project, or which cross-module contracts escape Application.Abstractions and the merged Application? Look at the Application project files beside the shared contracts.

Marked guidance: a reviewer answers this question.

Mirror Modules in Folders and Namespaces#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#mirror-modules-in-folders-and-namespaces
Statement

Every project MUST give each module one folder and namespace.

Why

The folder name derives from the module identifier, so module/spending owns the Spending folder in every project, and the namespace follows the folder under litespec/workspace/naming#keep-namespaces-aligned-with-folders. The same module names in every layer let a reader move from a Domain aggregate to its handler, its repository, and its endpoint without a map. This provision states the five-project form of the module organization that litespec/backend/architecture#organize-code-by-module-aggregate-and-use-case states for the module layout.

Example

The Spending module owns one folder in every project, and each namespace follows its folder:

Text
Claimwick.Domain/Spending/Expenses/Expense.cs
Claimwick.Application/Spending/Expenses/SubmitExpense/SubmitExpenseCommandHandler.cs

The handler above lives in namespace Claimwick.Application.Spending.Expenses.SubmitExpense.

Checka reviewer answers

Reviewed forWhich projects miss one folder and namespace per module, or name one outside its module? Look at the module folders and namespaces in every project.

Marked guidance: a reviewer answers this question.

Use the Project Reference Graph#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#use-the-project-reference-graph
Statement

A project reference MUST follow the graph in the table in this section and add no other edge.

Why

An undeclared reference lets a layer reach past its boundary, and review has no ground to refuse what no graph forbids. The table names every permitted edge, so composition stays in the hosts and each project names only the layers its job needs. A small project merged under litespec/backend/dotnet/ext/layer-projects#use-five-application-projects has no Application.Abstractions row; its remaining rows still bind. This provision states the project form of the layer graph that litespec/workspace/dependencies#follow-the-declared-dependency-graph declares.

Example

Projectmay reference
DomainNone
Application.AbstractionsDomain
ApplicationDomain, Application.Abstractions
InfrastructureDomain, Application.Abstractions, Application
WebApiApplication.Abstractions, Application, Infrastructure
WorkerApplication.Abstractions, Application, Infrastructure
AppHostDeployable host projects through Aspire resource references
Test projectsOnly production projects and test support needed by their test category

WebApi references Infrastructure for composition in Program.cs. Endpoint implementations take their dependencies from Application abstractions, not from Infrastructure repositories, sessions, or provider clients.

Checka reviewer answers

Reviewed forWhich project files add a reference outside the permitted edges of the graph table? Look at each project file beside the graph table.

Marked guidance: a reviewer answers this question.

Keep Domain Package-Free#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#keep-domain-package-free
Statement

The Domain project MUST reference no package beyond the .NET base class library.

Why

A package in Domain makes business rules depend on a framework, a provider, or a messenger. Marten, EF Core, LiteBus, ASP.NET Core, injection, serialization, and logging packages all stay outside, so Domain builds on the base class library alone. This provision states the .NET form of litespec/workspace/dependencies#keep-domain-dependency-free.

Checka reviewer answers

Reviewed forWhich Domain project files reference a package beyond the base class library? Look at the Domain project file.

Marked guidance: a reviewer answers this question.

Keep Application Dependencies Narrow#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#keep-application-dependencies-narrow
Statement

Application MUST reference only LiteBus command and query abstractions, required read abstractions, and needed Microsoft abstractions, plus the IQuerySession read boundary.

Why

Application coordinates; Infrastructure hides technology. A provider package in Application makes the coordination layer depend on the technology it was meant to stay free of. The Statement names the one permitted read boundary; everything else provider-shaped stays out. This provision states the .NET form of litespec/workspace/dependencies#keep-application-free-of-provider-packages.

Checka reviewer answers

Reviewed forWhich Application project files reference beyond the permitted abstractions and the one read boundary? Look at the Application project file.

Marked guidance: a reviewer answers this question.

Keep the Test Projects the Size Requires#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#keep-the-test-projects-the-size-requires
Statement

A backend solution MUST hold exactly the test projects its size requires: Architecture and Integration for small, adding Acceptance for large.

Why

The required suites match the size: Architecture and Integration for small, adding Acceptance for large. Unit test projects stay optional in both sizes, and Acceptance stays optional for small. An Acceptance project without the executable BDD extension is an unnamed baseline project: select the extension, or name the project for what it actually is. This provision states the .NET form of litespec/backend/testing#keep-the-test-suites-the-size-requires.

Example

A test suite can contain:

Text
{ProjectName}.Architecture.Tests/
{ProjectName}.Integration.Tests/
{ProjectName}.Acceptance.Tests/

A small solution holds the first two; Acceptance appears when the executable BDD extension activates.

Checka reviewer answers

Reviewed forWhich solution trees miss Architecture or Integration test projects, or add Acceptance outside size large? Look at apps/api/tests/ beside the declared size.

Marked guidance: a reviewer answers this question.

Keep .NET Production and Test Projects Separate#

ruleruleguidance
litespec/backend/dotnet/ext/layer-projects#keep-net-production-and-test-projects-separate
Statement

Production projects MUST live under apps/api/src/ and test projects under apps/api/tests/, with the solution directly under apps/api/.

Why

Production under apps/api/src/ and tests under apps/api/tests/ make a test dependency reaching production visible in the path itself. The solution sits directly under apps/api/, beside both. A workspace AppHost is the one production project outside src/, placed by litespec/backend/dotnet/workspace#place-a-workspace-apphost-beside-the-applications-it-runs. This provision states the .NET form of litespec/workspace/structure#keep-production-and-test-code-separate.

Example

The baseline tree is:

Text
apps/api/
  {ProjectName}.slnx
  Directory.Build.props
  Directory.Packages.props
  src/
    {ProjectName}.Domain/
    {ProjectName}.Application.Abstractions/
    {ProjectName}.Application/
    {ProjectName}.Infrastructure/
    {ProjectName}.WebApi/
    {ProjectName}.Worker/             when background work needs a host
    {ProjectName}.AppHost/            when an orchestration host runs the API alone
    {ProjectName}.ServiceDefaults/    when a local orchestration host is used
  tests/
    {ProjectName}.Architecture.Tests/
    {ProjectName}.Integration.Tests/
    {ProjectName}.Acceptance.Tests/   when size is large
    {ProjectName}.Domain.Tests/       optional unit tests
    {ProjectName}.Application.Tests/  optional unit tests

Worker is conditional on the extension that runs background work. Acceptance.Tests is required when size is large and optional otherwise. Domain.Tests and Application.Tests are optional in both sizes. AppHost and ServiceDefaults are local hosting and diagnostics projects rather than application layers. An application that orchestrates its local dependencies another way ships neither and records no exception for their absence.

Checka reviewer answers

Reviewed forWhich production or test project files, or solution files, sit outside their fixed address? Look at apps/api/.

Marked guidance: a reviewer answers this question.

Conventions#

Use Five Application Projects#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-five-application-projects
Default

Use Domain, Application.Abstractions, Application, Infrastructure, and WebApi as the five baseline projects at every size, with a small project merging Application.Abstractions into Application and hosts and a Worker project as allowed extras.

ReplacementA project records its own project layout in its host documentation.

Why

Application.Abstractions holds the contracts that more than one module names: the pipeline's own positions, the cross-module ports, the transport-neutral failures, and the published integration events. It references Domain and the required LiteBus module abstractions, and nothing else, so no type in it can name a use case, a handler, or a module.

That refusal is why it is a project rather than a folder. A folder inside Application cannot decline what a developer puts in it, and the folder that tries becomes the place every unplaced type goes. A small project with few cross-module contracts may merge them into Application, trading that protection for one less project.

An implementation never belongs there. The project name states the test, and a type that cannot pass it belongs to a module under litespec/backend/architecture#place-a-type-at-the-lowest-folder-that-holds-its-consumers.

Hosts support local hosting and diagnostics. They are hosts rather than layer projects. A Worker project hosts background work under litespec/backend/architecture#create-a-worker-project-when-background-work-needs-its-own-process.

Example

Text
apps/api/src/{ProjectName}.Domain/
apps/api/src/{ProjectName}.Application.Abstractions/
apps/api/src/{ProjectName}.Application/
apps/api/src/{ProjectName}.Infrastructure/
apps/api/src/{ProjectName}.WebApi/
Checka reviewer answers

Reviewed forWhich projects sit outside the five baseline projects, the merged small layout, or the allowed hosts and Worker? Look at the solution file beside the declared project size.

Marked guidance: a reviewer answers this question.

Use This Baseline Package Ownership#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-this-baseline-package-ownership
Default

Assign each baseline package to the owning layer named in the table in this section.

ReplacementA project names the owning layer of each baseline package in its project documentation.

Why

Without an owner, a package lands in the project that first needs it and spreads from there. The table assigns each baseline package to its one owning project, so review can refuse it anywhere else. This provision states the .NET form of litespec/workspace/dependencies#assign-each-baseline-package-to-its-owning-layer.

Example

ProjectDirect package groups
DomainNone
Application.AbstractionsRequired LiteBus module abstractions only
ApplicationRequired LiteBus module abstractions; Marten for the selected IQuerySession read boundary; Microsoft abstractions used by public ports
InfrastructureMarten; LiteBus registration modules. Provider, resilience, service discovery, and configuration packages required by implemented adapters
WebApiJWT Bearer [REDACTED], ASP.NET Core OpenAPI, build-time OpenAPI generation, Scalar, and required LiteBus mediator abstractions
WorkerLiteBus registration modules; the provider, resilience, and configuration packages its hosted operations need
ServiceDefaultsHealth checks, service discovery, HTTP resilience, and OpenTelemetry registration and instrumentation
AppHostAspire AppHost and resource hosting packages
Domain.TestsxUnit v3, assertions, and FsCheck
Application.TestsxUnit v3, assertions, and NSubstitute
Integration.TestsxUnit v3, assertions, test host, Testcontainers PostgreSQL, and the Microsoft.Testing.Platform report and coverage extensions
Architecture.TestsxUnit v3 and assertions
Acceptance.TestsxUnit v3, assertions, the behavior runner its extension selects, test host, and the report and coverage extensions

Test projects reference xUnit v3 and run on Microsoft.Testing.Platform. The consumer global.json sets "test": { "runner": "Microsoft.Testing.Platform" }, because the .NET 10 dotnet test command refuses the VSTest mode for these projects. Domain.Tests carries FsCheck at the 3.6.0 pin litespec.manifest.json states for the property tests that litespec/backend/testing#prove-every-calculation-with-property-tests names and the model-based tests that litespec/backend/testing#prove-every-aggregate-with-a-model-based-test names.

The example adds a package to the narrowest owning project. A central version entry does not authorize every project to reference that package. Conditional packages such as EF Core, Reqnroll, LightBDD, SignalR, and WireMock are referenced only after their extension activates. The manifest pins no architecture-test library; a project that wants one adds it with owner approval.

Checka reviewer answers

Reviewed forWhich baseline package references sit outside the project the ownership table assigns? Look at each package reference beside the ownership table.

Marked guidance: a reviewer answers this question.

Run LiteSpec.Analyzers in Every Application Project#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#run-litespecanalyzers-in-every-application-project
Default

Reference LiteSpec.Analyzers at the manifest pin, with PrivateAssets="all", from every Domain, Application.Abstractions, Application, Infrastructure, and WebApi project.

ReplacementA project names the projects its analyzer run covers in its project documentation.

Why

The analyzers report a breach of a .NET provision in the editor and the build, at the line that breaks it, with a link to the provision. Without them, a breach surfaces only in review, if at all. The package is a development dependency: it adds nothing to the built assemblies, so Domain stays package-free at run time. LS2016 reports a pipeline-owned collaborator in a handler constructor; its default list covers the LiteBus unit of work, idempotency store, and outbox, and a project adds its uniqueness claim service. Check .NET code with the analyzers sets it up, and Analyzer rules lists what each rule reports.

Example

XML
<ItemGroup>
  <PackageReference Include="LiteSpec.Analyzers" PrivateAssets="all" />
</ItemGroup>
Checka reviewer answers

Reviewed forWhich layer project files miss LiteSpec.Analyzers at the manifest pin with PrivateAssets="all"? Look at each layer project file.

Marked guidance: a reviewer answers this question.

Use the Domain Module Layout#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-the-domain-module-layout
Default

Give each aggregate a folder named with its plural root name, holding its states, events, exceptions, and concepts in the files this provision names.

ReplacementA project names its Domain module layout in its Domain documentation.

Why

Each aggregate folder holds everything one aggregate owns in the files the layout names, so a reader finds the full aggregate in one place. Namespaces follow the folders under litespec/workspace/naming#keep-namespaces-aligned-with-folders, and each file sits at the lowest folder holding its consumers under litespec/backend/architecture#place-a-type-at-the-lowest-folder-that-holds-its-consumers. This provision states the .NET form of litespec/backend/domain#organize-a-module-by-aggregate-and-concept.

Example

The Domain module layout is:

Text
{ProjectName}.Domain/
  Shared/
    AggregateRoot.cs
    Entity.cs
    IAggregateRoot.cs
    IDomainEvent.cs
    StronglyTypedIds/
      IStronglyTypedId.cs
    Exceptions/
      DomainException.cs
  Posts/                          one aggregate whose name matches the module: kept flat in the module folder
    Post.cs
    PostId.cs
    PostTitle.cs
    IPostRepository.cs
    States/
      PostState.cs
      PostDraftState.cs
      PostPublishedState.cs
      PostArchivedState.cs
    Events/
      PostCreatedEvent.cs
      PostPublishedEvent.cs
    Exceptions/
      PostIdentityRequiredException.cs
      PostAlreadyPublishedException.cs
  Shipping/                       one aggregate whose name differs from the module, with a domain union in a concept folder
    Shipments/
      Shipment.cs
      ShipmentId.cs
      IShipmentRepository.cs
      Outcomes/
        ShipmentOutcome.cs
        ShipmentDeliveredOutcome.cs
        ShipmentReturnedOutcome.cs
        ShipmentLostOutcome.cs
      States/
        ShipmentState.cs
        ShipmentPendingState.cs
        ShipmentDispatchedState.cs
        ShipmentDeliveredState.cs
      Events/
        ShipmentOutcomeRecordedEvent.cs
  Customers/                      more than one aggregate: one plural folder per aggregate
    CustomerAccounts/               namespace Shop.Domain.Customers.CustomerAccounts
      CustomerAccount.cs
      CustomerAccountId.cs
      ICustomerAccountRepository.cs
      States/
        CustomerAccountState.cs
        CustomerAccountClaimedState.cs
      Events/
        CustomerAccountRestrictedEvent.cs
    Consents/                       namespace Shop.Domain.Customers.Consents
      Consent.cs
      ConsentId.cs
      IConsentRepository.cs
      States/
        ConsentState.cs
        ConsentGrantedState.cs
      Events/
        ConsentGrantedEvent.cs

Each aggregate-specific repository interface stays with the aggregate it loads. Application, Infrastructure, and WebApi use the same module and per-aggregate folder names, per litespec/backend/dotnet/ext/layer-projects#mirror-modules-in-folders-and-namespaces.

Checka reviewer answers

Reviewed forWhich Domain module folders break the one plural folder per aggregate with states, events, exceptions, and concepts? Look at each module folder.

Marked guidance: a reviewer answers this question.

Use the Operation Folder Layout#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-the-operation-folder-layout
Default

Place each operation in a folder named for its use case under its module and aggregate, with the files this provision names.

ReplacementA project names its operation layout in its Application documentation.

Why

The folder path matches the specification path, so a reader locates an operation from its use-case identifier without searching. Type names derive from the use-case identifier with the architectural suffix, so use-case/spending.submit-expense owns SubmitExpenseCommand under litespec/workspace/naming#use-architectural-suffixes. Only a use case or a handled fact gets a folder: any other type sits flat in the folder that owns it under litespec/backend/architecture#place-a-type-at-the-lowest-folder-that-holds-its-consumers, and a handled fact's folder takes the On{Fact} name with its marked handler classes. Each result type carries one case per result, and each refusal carries the refusal code from its result record. A query result type carries one member per declared include group, and each result part is named after its result under litespec/workspace/naming#name-result-parts-after-their-result.

Example

The operation layout is:

Text
{ProjectName}.Application.Abstractions/     contracts more than one module names
  Clock/
    IClock.cs
  Failures/
    ResourceNotFoundException.cs
    UseCaseForbiddenException.cs
    UseCaseConflictException.cs
  Validation/
    ValidationErrors.cs
{ProjectName}.Application/
  Pipeline/                         the pipeline's own stages, and nothing else
    ApplicationPipeline.cs
    AuthorizationGuard.cs
  Posts/                            single aggregate whose name matches the module: operations sit directly under the module
    IPostArchive.cs                 a port several Posts use cases name
    CreateDraft/
      CreateDraftCommand.cs
      CreateDraftCommandResult.cs
      CreateDraftCommandValidator.cs
      CreateDraftCommandHandler.cs
    GetPost/
      GetPostQuery.cs
      GetPostQueryResult.cs
      GetPostQueryValidator.cs
      GetPostQueryHandler.cs
    ListPosts/
      ListPostsQuery.cs
      ListPostsQueryResult.cs
      ListPostsQueryResultItem.cs
      ListPostsQueryValidator.cs
      ListPostsQueryHandler.cs
    OnPostPublished/   event handlers of one fact: one marked class per reaction
      NotifySubscribersOnPostPublishedHandler.cs
      IPostPublicationNotifier.cs   a port one event handler names
  Customers/                        two aggregates: operations nest under each aggregate
    CustomerAccounts/
      CustomerAccountScopeLookup.cs aggregate-owned, so it sits with the aggregate
      RestrictAccount/
        RestrictAccountCommand.cs
        RestrictAccountCommandHandler.cs
    Consents/
      GrantConsent/
        GrantConsentCommand.cs
        GrantConsentCommandHandler.cs

Module and aggregate order, including the single-aggregate flat form, follow litespec/backend/architecture#organize-code-by-module-aggregate-and-use-case.

Checka reviewer answers

Reviewed forWhich operation folders miss message, result, validator, or handler files named for the use case, or which event handlers escape their On{Fact} folder? Look at each operation folder.

Marked guidance: a reviewer answers this question.

Use the Endpoint Folder Layout#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-the-endpoint-folder-layout
Default

Place each endpoint in a folder named for its use case under its module and aggregate, with the files this provision names.

ReplacementA project names its endpoint layout in its WebApi documentation.

Why

The folder path matches the specification path, so a reader locates an operation from its use-case identifier without searching. Transport types take the names their boundary roles assign under litespec/backend/api#name-transport-types-by-their-boundary-role, and transport mapping lives in WebApi under litespec/backend/application#keep-mappings-at-the-owning-boundary. Response models carry the query's declared include groups. Module nesting, including the single-aggregate flat form, follows litespec/backend/architecture#organize-code-by-module-aggregate-and-use-case.

Example

The endpoint layout is:

Text
{ProjectName}.WebApi/
  Endpoints/
    Posts/                          single aggregate whose name matches the module
      CreateDraft/
        CreateDraftEndpoint.cs
        CreateDraftRequestModel.cs
        CreateDraftResponseModel.cs
        CreateDraftApiMappings.cs
      GetPost/
        GetPostEndpoint.cs
        GetPostResponseModel.cs
        GetPostApiMappings.cs
    Customers/                      two aggregates: operations nest under each aggregate
      CustomerAccounts/
        RestrictAccount/
          RestrictAccountEndpoint.cs
          RestrictAccountRequestModel.cs
          RestrictAccountApiMappings.cs
      Consents/
        GrantConsent/
          GrantConsentEndpoint.cs
          GrantConsentRequestModel.cs
          GrantConsentApiMappings.cs
  Errors/
    GlobalExceptionHandler.cs
    ProblemDetailsApiMappings.cs
  Security/
    CurrentActor.cs
  OpenApi/
  Program.cs
Checka reviewer answers

Reviewed forWhich endpoint folders miss the endpoint, request and response models, or mappings named for the use case? Look at each endpoint folder.

Marked guidance: a reviewer answers this question.

Use This Infrastructure Layout#

conventiondefaultguidance
litespec/backend/dotnet/ext/layer-projects#use-this-infrastructure-layout
Default

Place Marten configuration, repositories, commit behavior, and serialization under one Infrastructure persistence folder.

ReplacementA project names the folder holding its persistence types in its project documentation.

Why

One persistence folder holds Marten configuration, repositories, commit behavior, and serialization, so every persistence type has one discoverable home. Scattered configuration hides the provider setup across the project. This provision states the .NET form of litespec/backend/persistence#use-one-persistence-folder.

Example

Text
{ProjectName}.Infrastructure/
  Persistence/
    Marten/
      MartenServiceRegistration.cs
      MartenStoreConfiguration.cs
      Commands/
        SaveChangesCommandPostHandler.cs
      Events/
        DomainEventBuffer.cs
      Posts/                        single aggregate whose name matches the module: files directly under the module
        PostRepository.cs
        PostMartenConfiguration.cs
      Customers/                    two aggregates: one folder per aggregate
        CustomerAccounts/
          CustomerAccountRepository.cs
          CustomerAccountMartenConfiguration.cs
        Consents/
          ConsentRepository.cs
          ConsentMartenConfiguration.cs

The module folders follow litespec/backend/architecture#organize-code-by-module-aggregate-and-use-case. A single-aggregate module stays flat when its aggregate root's plural name equals the module name. For example, Post stays flat under Posts. Otherwise, each aggregate takes its own folder.

Aggregate-specific configuration stays beside its aggregate, while session and commit plumbing stay under the Marten root.

Checka reviewer answers

Reviewed forWhich Marten configuration, repository, commit, or serialization files sit outside the one Infrastructure persistence folder? Look at the persistence files.

Marked guidance: a reviewer answers this question.

Dependencies#

Packages: None.

Related extensions: the EF Core Persistence, for aggregates needing relational storage.

Guides: None.