Sample Clean Architecture .NET 8 WebApi demonstrating the Axlis Sitecore Headless GraphQL ORM.
Note: This project demonstrates production-ready patterns for integrating Axlis into a Clean Architecture solution. Axlis-specific documentation uses the .AXLIS.md suffix (e.g., CHANGELOG.AXLIS.md, CONTRIBUTING.AXLIS.md).
Axlis.CleanArchitecture.Sample is a working example of integrating the Axlis ORM into a Clean Architecture ASP.NET Core WebApi. It demonstrates:
- Wiring Axlis and Axlis.GraphQL services via dependency injection
- Defining strongly-typed Sitecore template POCOs
- Using the Axlis facade (
ISitecoreFacade) in CQRS handlers - Exercising field types: TextField, ImageField, MultilistField, ItemReferenceField
- Axes traversal: Parent, Children, Siblings, GetChildren, GetDescendants
- The WithResult rich API: value, metadata, and diagnostics
This is a sample project, not a template. It demonstrates production-ready patterns for integrating Axlis into a Clean Architecture solution. All Axlis packages are published to NuGet.org at v0.1.0, so you can use this as a reference for your own projects.
The solution follows Clean Architecture with strict dependency rules. Axlis is wired in the WebApi composition root and consumed by the Application layer via ISitecoreFacade.
┌──────────────────────────────────────────────┐
│ WebApi │ Composition root · Axlis DI · user-secrets
├──────────────────────────────────────────────┤
│ Presentation │ Controllers · SitecoreController (/v1/sitecore)
├──────────────────────────────────────────────┤
│ Application │ CQRS handlers · Sitecore template POCOs
│ └── Sitecore/ │ Disclaimer, HomePage, DictionaryRoot, Style
├──────────────────────────────────────────────┤
│ Infrastructure │ DateTimeProvider · Operational wiring
├──────────────────────┬───────────────────────┤
│ Domain │ Shared │ BaseEntity · IAggregateRoot │ Enums
└──────────────────────┴───────────────────────┘
| Layer | Project | Axlis Integration |
|---|---|---|
| WebApi | CleanArchitecture.WebApi |
Registers AddAxlis() and AddAxlisGraphQL(), wires UseAxlis(), reads config from appsettings + user-secrets |
| Presentation | CleanArchitecture.Presentation |
SitecoreController exposes /v1/sitecore/showcase endpoint |
| Application | CleanArchitecture.Application |
Defines Sitecore template POCOs, CQRS handler uses ISitecoreFacade |
| Infrastructure | CleanArchitecture.Infrastructure |
No Axlis code (clean separation) |
| Domain | CleanArchitecture.Domain |
No Axlis code (pure domain) |
| Shared | CleanArchitecture.Shared |
No Axlis code (shared types) |
Axlis.CleanArchitecture.Sample/
├── src/
│ ├── CleanArchitecture.Domain/ # BaseEntity, IAggregateRoot
│ ├── CleanArchitecture.Shared/ # Enums, constants
│ ├── CleanArchitecture.Application/ # CQRS handlers, Sitecore template POCOs
│ │ ├── Api/
│ │ │ ├── Samples/ # PowerCSharp Cache sample endpoints
│ │ │ └── Sitecore/ # Axlis showcase: Query, Handler, Response
│ │ ├── Sitecore/
│ │ │ └── Templates/ # Disclaimer, HomePage, DictionaryRoot, Style
│ │ └── Common/ # Behaviors, BaseRequestHandler
│ ├── CleanArchitecture.Operational/ # Polly retry policies
│ ├── CleanArchitecture.Infrastructure/ # DateTimeProvider
│ ├── CleanArchitecture.Presentation/ # Controllers, ApiResponse<T>
│ │ └── Controllers/v1/
│ │ ├── SamplesController # PowerCSharp Cache demo
│ │ └── SitecoreController # Axlis showcase endpoint
│ └── CleanArchitecture.WebApi/ # Program.cs (Axlis DI, user-secrets)
├── tests/
│ ├── CleanArchitecture.Tests.Shared/
│ ├── CleanArchitecture.WebApi.UnitTests/
│ └── CleanArchitecture.WebApi.IntegrationTests/
├── .github/
│ └── workflows/
│ └── ci.yml
├── Directory.Build.props
├── global.json
└── PowerCSharp.CleanArchitecture.sln
- Axlis service registration in
Program.csviaAddAxlis()andAddAxlisGraphQL() - Ambient lazy-loader wired via
UseAxlis()for ExtendedItem.Axes traversal - User-secrets for sensitive config (Endpoint, ApiKey) — never committed
- NuGet packages at v0.1.0 (Axlis, Axlis.Abstractions, Axlis.Core, Axlis.GraphQL) from nuget.org
- Configurable caching via PowerCSharp.Feature.Cache providers (BitFaster, Disk)
- Diagnostic support via EnableDiagnostics option for troubleshooting
Located in src/CleanArchitecture.Application/Sitecore/Templates/:
System Templates:
- Language — Language settings including charset, encoding, ISO codes, and fallback language
- MainSection — Base system template (foundation for other templates)
- Node — Base template for hierarchical structures
- PublishingTarget — Publishing target database configuration
Sample Templates:
- SampleItem — Demonstrates TextField usage with Title and Text fields
All template classes include comprehensive XML documentation explaining their purpose and field mappings.
GET /v1/sitecore/showcase exercises six Axlis API pivots:
- TextField — SampleItem.Title and SampleItem.Text field access
- Axes traversal — Parent, Children, Grandparent, Siblings navigation
- GetDescendants — Recursive traversal with template type filtering (Language items)
- WithResult rich API — Metadata (ItemId, ItemPath, ItemVersion, Timestamp) and Diagnostics (warnings, errors, info)
- Lazy-loading — Demonstrates Axes lazy-fetch behavior for items beyond initial fetch
- Caching — Shows how Axlis caching integrates with the facade
The handler includes comprehensive developer notes explaining each API pattern, performance considerations, and best practices.
- Strict dependency rule enforced by project references
- CQRS via MediatR with
BaseRequestHandler<T, TResponse> - FluentValidation integration
ApiResponse<T>envelope on all endpointsLoggingBehavior<TRequest, TResponse>pipeline behavior
- CORS, Cache (BitFaster), DiskCache, Samples feature modules
- Flag-gating via
PowerFeatures:<Key>:Enabledinappsettings.json - Samples feature enabled in Development for showcase endpoint access
| Tool | Version |
|---|---|
| .NET SDK | 8.0 or later |
| Git | any recent version |
| Sitecore Headless GraphQL endpoint | accessible from dev machine |
git clone https://github.com/marioarce/Axlis.CleanArchitecture.Sample.git
cd Axlis.CleanArchitecture.Sample
dotnet restore
dotnet buildcd src/CleanArchitecture.WebApi
dotnet user-secrets set "AxlisGraphQL:Endpoint" "https://your-sitecore-instance/sitecore/api/graph/edge"
dotnet user-secrets set "AxlisGraphQL:ApiKey" "{YOUR-API-KEY}"dotnet run --project src/CleanArchitecture.WebApi/CleanArchitecture.WebApi.csprojThe API starts on https://localhost:7xxx / http://localhost:5xxx. Open the Swagger UI at /swagger.
curl "https://localhost:7235/v1/sitecore/showcase?rootPath=/sitecore/content/home"Adjust rootPath to match your Sitecore content tree structure.
# All tests
dotnet test
# Unit tests only
dotnet test tests/CleanArchitecture.WebApi.UnitTests
# Integration tests only
dotnet test tests/CleanArchitecture.WebApi.IntegrationTestsIntegration tests use WebApplicationFactory<Program> — no external dependencies required.
appsettings.json contains the Axlis and AxlisGraphQL sections:
{
"Axlis": {
"CacheTtl": "00:30:00",
"EnableDiagnostics": true
},
"AxlisGraphQL": {
"Endpoint": "",
"BatchSize": 10,
"TimeoutSeconds": 30
}
}The Endpoint and ApiKey values are intentionally empty in appsettings.json. Set them via user-secrets:
dotnet user-secrets set "AxlisGraphQL:Endpoint" "https://your-sitecore-instance/sitecore/api/graph/edge"
dotnet user-secrets set "AxlisGraphQL:ApiKey" "{YOUR-API-KEY}"The Samples feature flag must be enabled to access the showcase endpoint:
{
"PowerFeatures": {
"Samples": {
"Enabled": true
}
}
}This is already set to true in appsettings.json for this sample.
This sample uses the published NuGet packages from nuget.org:
Axlisv0.1.0Axlis.Abstractionsv0.1.0Axlis.Corev0.1.0Axlis.GraphQLv0.1.0
All packages are configured in the respective project files:
src/CleanArchitecture.Application/CleanArchitecture.Application.csproj— referencesAxlis.Coresrc/CleanArchitecture.WebApi/CleanArchitecture.WebApi.csproj— referencesAxlisandAxlis.GraphQL
The sample template POCOs use placeholder GUIDs. Replace these with the real template IDs from your Sitecore instance:
System Templates:
Language.cs—{F68F13A6-3395-426A-B9A1-FA2DC60D94EB}MainSection.cs—{E3E2D58C-DF95-4230-ADC9-279924CECE84}Node.cs—{239F9CF4-E5A0-44E0-B342-0F32CD4C6D8B}PublishingTarget.cs—{E130C748-C13B-40D5-B6C6-4B150DC3FAB3}
Sample Templates:
SampleItem.cs—{76036F5E-C477-44E2-8178-773413C533F7}
To find your template GUIDs in Sitecore:
- Navigate to the template in the Content Editor
- Click the item and view the Quick Info section
- Copy the ID value
For detailed Axlis documentation, see:
Clean API vs Rich API:
GetItemByPathAsync<T>()— Returns T? (null if not found). Use for simple fetches.GetItemByPathWithResultAsync<T>()— ReturnsAxlisResult<T>with Value, Metadata, and Diagnostics. Use for troubleshooting or when you need provenance data.
Lazy Loading:
- Axes traversal (Parent, Children, Siblings) may trigger lazy-fetches if data wasn't included in the initial GraphQL response
- Lazy fetches are synchronous (property getter cannot be async)
- Safe in ASP.NET Core, but avoid deep traversal in non-ASP environments
- Use
GetItemsByPathsAsyncfor batch pre-fetch in performance-critical scenarios
Caching:
- Powered by
ICacheServicefrom PowerCSharp.Feature.Cache.Abstractions - Dual-indexing (by ID and path) with null-safety (null results never cached)
- Configure CacheTtl in
AddAxlisoptions (default: 60 minutes) - Use
SitecoreItemCacheManager.InvalidateAsync(key)to evict specific items
Field Types:
TextField— Simple text fieldsImageField— Image metadata (Src, Alt, etc.)MultilistField— Multi-select item referencesItemReferenceField— Single item reference- All fields inherit from
BaseFieldwith FieldName, RawValue, and IsEmpty
This project is licensed under the MIT License.
README.md— This file (main project documentation)CHANGELOG.AXLIS.md— Axlis-specific changelogCONTRIBUTING.AXLIS.md— Contribution guidelines for this sampleSECURITY.AXLIS.md— Security considerations for Axlis integration