Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,27 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

## [0.1.0-preview] — TBD
## [0.2.0] — 2026-07-10

### Changed

- **BREAKING:** Renamed the entire package family for ecosystem clarity ahead of additional Axlis components (`Axlis.Context`, `Axlis.Diagnostics`, `Axlis.Caching`):
- `Axlis` → `Axlis.ORM`
- `Axlis.Core` → `Axlis.ORM.Core`
- `Axlis.GraphQL` → `Axlis.ORM.GraphQL`
- `Axlis.Abstractions` → `Axlis.ORM.Abstractions`
- Namespaces updated throughout to match the new package names (e.g. `namespace Axlis.Core;` → `namespace Axlis.ORM.Core;`).
- DI extension methods renamed: `AddAxlis()` → `AddAxlisORM()`, `AddAxlisGraphQL()` → `AddAxlisORMGraphQL()`. `UseAxlis()` is unchanged.
- Solution restructuring: `Axlis.ORM.sln` is now the live, buildable solution (4 src + 3 test projects); `Axlis.sln` repurposed as empty scaffolding reserved for future ecosystem components.
- `docs/` reorganized: ORM-specific guides moved under `docs/orm/` (`Architecture.md`, `GettingStarted.md`, `Templates.md`, `Axes.md`, `Caching.md`); added `docs/WORKFLOW.md` (GitFlow SOP).
- CI/CD workflows updated to build/test/pack `Axlis.ORM.sln`.
- Per-package `README.md`s rewritten for the new package identities.

> **Note on versioning:** this tag was not accompanied by a `PackageVersion` bump — `Directory.Build.props` still reports `0.1.0` for every package, so the artifacts published under this milestone are `Axlis.ORM.*` version `0.1.0` (new package IDs at their first version), not a distinct `0.2.0` release. The `v0.2.0` git tag marks the rename in history; it does not correspond to a `0.2.0` NuGet version. A real version bump is still owed before the next tag.

---

## [0.1.0] — 2026-07-08

### Added

Expand Down Expand Up @@ -58,5 +78,6 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

[Unreleased]: https://github.com/marioarce/Axlis/compare/v0.1.0-preview...HEAD
[0.1.0-preview]: https://github.com/marioarce/Axlis/releases/tag/v0.1.0-preview
[Unreleased]: https://github.com/marioarce/Axlis/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/marioarce/Axlis/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/marioarce/Axlis/releases/tag/v0.1.0
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Your responsibilities in this repo:

## 2. Non-Negotiable: De-Branding

Axlis.Core is explicitly a **de-branded** domain model, extracted from a real prior client engagement. **This is a public GitHub repository.** Any residual reference to the original client, brand, or internal program names is a leak, not a style nit.
Axlis.ORM.Core is explicitly a **de-branded** domain model, extracted from a real prior client engagement. **This is a public GitHub repository.** Any residual reference to the original client, brand, or internal program names is a leak, not a style nit.

- This rule applies **to the rule itself**. Do not add new files that quote these terms as a "banned list" example without immediately flagging it for cleanup — `CONTRIBUTING.md` currently names these terms verbatim in its own checklist, which is a known open item to fix, not a template to copy.
- Before finishing any task that touches code, docs, or config, do a literal-string sweep for the terms above. Treat a match as a blocking defect, not a lint warning.
Expand Down Expand Up @@ -83,7 +83,7 @@ Do not use these tags for casual questions or short answers — reserve them for

## 8. Tone

Precise, senior-engineer register. No hedging, no filler ("I think maybe we could possibly..."). Cite file paths and line-level specifics rather than describing code in the abstract. Name risk explicitly rather than burying it in a caveat at the end. Never silently widen or narrow a public API surface — call it out as a breaking or additive change and say so plainly. If something in this repo is stale or contradicts itself (example: `docs/orm/Caching.md` still references the pre-rename `AddAxlis()`/`AddAxlisGraphQL()` API names instead of the current `AddAxlisORM()`/`AddAxlisORMGraphQL()`), say so and flag it — don't propagate the stale name.
Precise, senior-engineer register. No hedging, no filler ("I think maybe we could possibly..."). Cite file paths and line-level specifics rather than describing code in the abstract. Name risk explicitly rather than burying it in a caveat at the end. Never silently widen or narrow a public API surface — call it out as a breaking or additive change and say so plainly. If something in this repo is stale or contradicts itself (this happened before: `docs/orm/Caching.md` referenced the pre-rename `AddAxlis()`/`AddAxlisGraphQL()` API names well after the code moved to `AddAxlisORM()`/`AddAxlisORMGraphQL()` — since fixed, but it's exactly the kind of drift to watch for), say so and flag it — don't propagate the stale name.

## 9. See Also

Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ Before marking a PR ready for review:
- [ ] `dotnet build --configuration Release` passes with **0 warnings, 0 errors**.
- [ ] `dotnet test --configuration Release` passes.
- [ ] All public APIs have XML doc comments.
- [ ] No Honda/Acura/Vehicle/ProductDivision/OnePlatform references in shipped code.
- [ ] No copyright references in shipped code.
- [ ] PR title follows the same Conventional Commit format.
- [ ] PR body includes `Closes #<N>` to auto-close the issue on merge.

---

## Code Style

- File-scoped namespaces (`namespace Axlis.Core;`).
- File-scoped namespaces (`namespace Axlis.ORM.Core;`).
- Private fields: `_camelCase`.
- `ConfigureAwait(false)` in all library `await` calls.
- Typed `ILogger<T>` — never `Console.Write` in library code.
Expand Down
2 changes: 1 addition & 1 deletion WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ This is the highest-blast-radius category of change in the repo — a regression
```
All three must be clean before proceeding — this mirrors exactly what `release.yml`'s `build-and-test` job will run.
6. **De-branding sweep across the full diff since the last tag**, not just this branch's commits — releases are the last line of defense before the code is public/downloadable as a package artifact.
7. **Documentation pass.** Confirm `docs/orm/*.md` and package `README.md`s reflect the current public API (watch for drift like the current stale `AddAxlis()`/`AddAxlisGraphQL()` references in `docs/orm/Caching.md` that predate the `*.ORM.*` rename — fix these as part of the release, don't ship known-stale docs).
7. **Documentation pass.** Confirm `docs/orm/*.md` and package `README.md`s reflect the current public API (this repo has shipped with stale naming before — `docs/orm/Caching.md` referenced pre-rename `AddAxlis()`/`AddAxlisGraphQL()` well after the `*.ORM.*` rename; now fixed, but re-check on every release, don't ship known-stale docs).
8. **PR `release/vX.Y.Z` → `main`.** Body includes release notes summarizing the changelog section. Requires review + passing status checks (branch protection).
9. **Merge triggers automation.** On merge to `main`: tag `vX.Y.Z` → `release.yml` runs `build-and-test` → `pack` → `publish` (NuGet.org + GitHub Packages via `dotnet nuget push --skip-duplicate`) → release branch auto-deleted per WORKFLOW.md.
10. **Verify published packages.** Confirm all four packages (`Axlis.ORM`, `Axlis.ORM.GraphQL`, `Axlis.ORM.Core`, `Axlis.ORM.Abstractions`) appear at the new version on both NuGet.org and GitHub Packages before announcing the release.
Expand Down
56 changes: 11 additions & 45 deletions docs/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,36 +102,18 @@ git push -u origin release/v0.2.0
- Deletes the release branch
- Main branch is tagged with release version

## 📋 Current Branch Structure
## 📋 Branch Structure (Illustrative)

```
main ← Production releases
├── develop ← Integration branch
├── feature/ecosystem-phase-1-renaming ← Current feature work
└── feature/future-features
└── release/v0.2.0 ← Release preparation (temporary)
├── feature/<N>-<short-description> ← active feature work
└── release/vX.Y.Z ← release prep (temporary)
```

## 🎯 Commit Message Convention

We use conventional commits for clarity with scope:

- `feat: scope: New features`
- `fix: scope: Bug fixes`
- `docs: scope: Documentation changes`
- `style: scope: Code formatting (no functional changes)`
- `refactor: scope: Code refactoring`
- `test: scope: Adding or updating tests`
- `chore: scope: Maintenance tasks`

### Examples
```bash
ecosystem: Rename projects to Axlis.ORM.*
ecosystem: Update namespaces to Axlis.ORM.*
ecosystem: Create Axlis.ORM.sln
fix: orm: Resolve null reference exception in ItemConverter
docs: orm: Update README with installation instructions
```
Commit format is defined once, in [`CONTRIBUTING.md`](../CONTRIBUTING.md#commit-format) — that file is the source of truth. In short: Conventional Commits with a mandatory GitHub issue scope, `<type>(#<issue>): <imperative summary>` (e.g. `fix(#41): correct IsFullyLoaded check when parent is root`). Don't duplicate the type/example list here — if it drifts from `CONTRIBUTING.md`, `CONTRIBUTING.md` wins.

## 🚀 CI/CD Automation

Expand Down Expand Up @@ -215,26 +197,10 @@ Release Branch → PR to main → Build/Test → Pack/Publish → Auto-delete re
7. **Test thoroughly before creating PRs**
8. **Release branches trigger automatic NuGet publishing**
9. **Branches are automatically deleted after successful merges**
10. **Use scope in commit messages** (e.g., `ecosystem`, `orm`, `context`)

## 🔄 Current Workflow Status

✅ **Completed:**
- Initial ORM implementation
- All projects created and configured
- Comprehensive tests
- CI/CD pipeline set up
- Ecosystem re-engineering planning completed

📋 **Ready for Use:**
- Complete GitFlow workflow with automation
- Release process with automatic NuGet publishing
- Branch cleanup and repository maintenance
- Full CI/CD pipeline with proper triggers

🎯 **Next Steps:**
1. Execute ecosystem re-engineering (Phase 1-7)
2. Create release branch for v0.2.0 when ready
3. Test complete workflow with actual release
4. Verify automatic NuGet publishing functionality
5. Validate branch cleanup automation
10. **Reference the GitHub issue in every commit** — `<type>(#N): ...` per `CONTRIBUTING.md`

## 🔄 Workflow Status

The GitFlow process, CI build/test pipeline, and tag-triggered NuGet publish (`release.yml`) described above are live and in active use — this is not aspirational. The `Axlis.ORM.*` package rename (tracked historically as the "ecosystem re-engineering" phases 1–7) is complete; see `CHANGELOG.md` for what shipped.

This section intentionally does not track a point-in-time task list — a process doc that says "next steps: do the rename" outlives the rename and goes stale (this section did exactly that once already). Track in-flight work via GitHub Issues/Projects, not here.
24 changes: 12 additions & 12 deletions docs/orm/Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ Axlis is a Sitecore Headless GraphQL ORM for .NET 8. It is structured as a famil

```
┌─────────────────────────────────────────────────────────┐
Axlis
│ Axlis.ORM
│ SitecoreFacade SitecoreItemCacheManager DI wiring │
└───────────────────────────┬─────────────────────────────┘
┌───────────────────────────▼─────────────────────────────┐
Axlis.GraphQL
│ Axlis.ORM.GraphQL │
│ HttpGraphQLTransport SitecoreService QueryBuilder │
└───────────────────────────┬─────────────────────────────┘
┌───────────────────────────▼─────────────────────────────┐
│ Axlis.Core
│ Axlis.ORM.Core │
│ Item ExtendedItem AxesAdapter ItemConverter │
│ Field types Built-in templates │
└───────────────────────────┬─────────────────────────────┘
┌───────────────────────────▼─────────────────────────────┐
Axlis.Abstractions
│ Axlis.ORM.Abstractions │
│ IItem ISitecoreFacade ISitecoreService │
│ IGraphQLTransport IAxlisDiagnosticsSink │
│ AxlisResult<T> SitecoreMetadata AxlisDiagnostics │
Expand All @@ -33,24 +33,24 @@ Axlis is a Sitecore Headless GraphQL ORM for .NET 8. It is structured as a famil

## Package Responsibilities

### `Axlis.Abstractions`
### `Axlis.ORM.Abstractions`
Contracts only. Targets `netstandard2.0` + `net8.0`. Zero runtime dependencies (beyond `Microsoft.Extensions.Logging.Abstractions`). Safe to reference from any layer including .NET Framework Sitecore projects.

### `Axlis.Core`
### `Axlis.ORM.Core`
De-branded Sitecore domain model. Converts raw GraphQL JSON into typed `Item` graphs. Provides the `ExtendedItem` base class, all field types, `AxesAdapter`, and built-in Sitecore templates.

### `Axlis.GraphQL`
### `Axlis.ORM.GraphQL`
Default `IGraphQLTransport` provider. Uses raw `HttpClient` + `System.Text.Json` — no third-party GraphQL library. Implements `ISitecoreService` with single-item and batch alias queries.

### `Axlis`
Top-level consumer package. Provides the `ISitecoreFacade` implementation, `SitecoreItemCacheManager`, `SitecoreItemLazyLoader`, and all `AddAxlis*` DI extension methods.
### `Axlis.ORM`
Top-level consumer package. Provides the `ISitecoreFacade` implementation, `SitecoreItemCacheManager`, `SitecoreItemLazyLoader`, and all `AddAxlisORM*` DI extension methods.

---

## Key Design Decisions

### Transport abstraction
`IGraphQLTransport` / `IGraphQLTransportFactory` live in `Axlis.Abstractions`, making the default `HttpClient` transport swappable. Consumers can register any custom `IGraphQLTransport`.
`IGraphQLTransport` / `IGraphQLTransportFactory` live in `Axlis.ORM.Abstractions`, making the default `HttpClient` transport swappable. Consumers can register any custom `IGraphQLTransport`.

### Two API flavors
Following the PowerCSharp Cache convention:
Expand All @@ -67,8 +67,8 @@ Following the PowerCSharp Cache convention:
`[SitecoreTemplate]` and `[SitecoreField]` attributes are codegen-ready today. A future Roslyn/CLI generator can introspect them without breaking changes.

### Target frameworks
- `Axlis.Abstractions` — `netstandard2.0;net8.0`
- `Axlis.Core`, `Axlis.GraphQL`, `Axlis` — `net8.0`
- `Axlis.ORM.Abstractions` — `netstandard2.0;net8.0`
- `Axlis.ORM.Core`, `Axlis.ORM.GraphQL`, `Axlis.ORM` — `net8.0`

---

Expand Down
4 changes: 2 additions & 2 deletions docs/orm/Axes.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ var allArticles = home?.Axes.GetDescendants(i =>

## How lazy-loading works

`GetItemByPathAsync` fetches a full item graph: 2 levels of ancestry (parent + grandparent) and the first 50 direct children. When `Axes.Parent` or `Axes.Children` are accessed, the data is already present in the item — no extra round-trip.
`GetItemByPathAsync` fetches a full item graph in one request: 2 levels of ancestry (parent + grandparent) and 2 levels of descendants (up to the first 50 children, and up to the first 50 grandchildren *per* child). When `Axes.Parent` or `Axes.Children` are accessed within that fetched depth, the data is already present in the item — no extra round-trip.

If an `Axes` member requests data that was **not** fetched (e.g. `Parent.Parent.Parent`, or more than 50 children), `SitecoreItemLazyLoader` transparently fetches the missing item via `ISitecoreService`, routing through `SitecoreItemCacheManager` to avoid redundant network calls.
If an `Axes` member requests data that was **not** fetched (e.g. `Parent.Parent.Parent`, more than 50 children/grandchildren at a level, or a third level of descendants), `SitecoreItemLazyLoader` transparently fetches the missing item via `ISitecoreService`, routing through `SitecoreItemCacheManager` to avoid redundant network calls.

```
item.Axes.Parent → data already in item (no fetch)
Expand Down
18 changes: 9 additions & 9 deletions docs/orm/Caching.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Caching

Axlis caching is powered by `ICacheService` from `PowerCSharp.Feature.Cache.Abstractions`. The default registration (`AddAxlis()`) installs a `NoOpCacheService` so the library works out-of-the-box without any cache dependency. For production use, swap in a real provider.
Axlis caching is powered by `ICacheService` from `PowerCSharp.Feature.Cache.Abstractions`. The default registration (`AddAxlisORM()`) installs a `NoOpCacheService` so the library works out-of-the-box without any cache dependency. For production use, swap in a real provider.

---

Expand Down Expand Up @@ -62,7 +62,7 @@ builder.Services.AddAxlis(o =>

### NoOp (default)

Registered automatically by `AddAxlis()` if no `ICacheService` is already registered. Does not cache anything — every request hits the GraphQL endpoint. Suitable for development or testing.
Registered automatically by `AddAxlisORM()` if no `ICacheService` is already registered. Does not cache anything — every request hits the GraphQL endpoint. Suitable for development or testing.

### BitFaster (recommended for production)

Expand All @@ -74,23 +74,23 @@ dotnet add package PowerCSharp.Feature.Cache.BitFaster

```csharp
builder.Services
.AddBitFasterCache() // registers ICacheService — must come BEFORE AddAxlis()
.AddAxlis()
.AddAxlisGraphQL(...);
.AddBitFasterCache() // registers ICacheService — must come BEFORE AddAxlisORM()
.AddAxlisORM()
.AddAxlisORMGraphQL(...);
```

### Custom provider

Implement `ICacheService` from `PowerCSharp.Feature.Cache.Abstractions` and register it before `AddAxlis()`:
Implement `ICacheService` from `PowerCSharp.Feature.Cache.Abstractions` and register it before `AddAxlisORM()`:

```csharp
builder.Services
.AddSingleton<ICacheService, MyCustomCacheService>()
.AddAxlis()
.AddAxlisGraphQL(...);
.AddAxlisORM()
.AddAxlisORMGraphQL(...);
```

Because ASP.NET Core DI resolves the **first** matching registration, any `ICacheService` registered before `AddAxlis()` takes precedence over the built-in `NoOpCacheService` fallback.
Because ASP.NET Core DI resolves the **first** matching registration, any `ICacheService` registered before `AddAxlisORM()` takes precedence over the built-in `NoOpCacheService` fallback.

---

Expand Down
Loading
Loading