diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 0000000..53719e2 --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,193 @@ +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "src/**/*.cs" + - "src/**/*.csproj" + - "Directory.Build.props" + - "Directory.Packages.props" + - "*.md" + - ".github/workflows/docs-deploy.yml" + release: + types: [published] + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: docs-deploy + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 20 + outputs: + version_path: ${{ steps.version.outputs.path }} + is_release: ${{ steps.version.outputs.is_release }} + version_tag: ${{ steps.version.outputs.tag }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-dotnet@v4 + with: + dotnet-version: "9.0.x" + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }} + restore-keys: nuget-${{ runner.os }}- + + - name: Restore solution + run: dotnet restore Compendium.sln + + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Determine target version path + id: version + run: | + if [ "${{ github.event_name }}" = "release" ]; then + TAG="${{ github.event.release.tag_name }}" + VERSION_NUM="${TAG#v}" + MAJOR_MINOR=$(echo "$VERSION_NUM" | cut -d. -f1-2) + echo "path=v$MAJOR_MINOR" >> "$GITHUB_OUTPUT" + echo "is_release=true" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + else + echo "path=main" >> "$GITHUB_OUTPUT" + echo "is_release=false" >> "$GITHUB_OUTPUT" + echo "tag=" >> "$GITHUB_OUTPUT" + fi + + - name: Sync ROADMAP into docs (if present) + run: | + if [ -f ROADMAP.md ]; then + { + echo '---' + echo 'uid: roadmap' + echo 'title: Roadmap' + echo '---' + echo + cat ROADMAP.md + } > docs/roadmap.md + echo "ROADMAP.md inlined into docs/roadmap.md" + else + echo "ROADMAP.md not found — keeping docs/roadmap.md placeholder" + fi + + - name: Generate API metadata + run: docfx metadata docs/docfx.json + + - name: Build documentation site + run: docfx build docs/docfx.json + + - name: Upload site artifact + uses: actions/upload-artifact@v4 + with: + name: docs-site + path: docs/_site/ + retention-days: 7 + + deploy: + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Download site artifact + uses: actions/download-artifact@v4 + with: + name: docs-site + path: site/ + + - name: Checkout or initialize gh-pages + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + mkdir -p gh-pages + cd gh-pages + git init -q -b gh-pages + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git remote add origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" + if git fetch origin gh-pages --depth=1 2>/dev/null; then + git reset --hard origin/gh-pages + echo "Existing gh-pages checked out." + else + echo "No gh-pages branch yet — bootstrapping." + fi + + - name: Deploy site to versioned path + env: + TARGET: ${{ needs.build.outputs.version_path }} + IS_RELEASE: ${{ needs.build.outputs.is_release }} + TAG: ${{ needs.build.outputs.version_tag }} + run: | + set -euo pipefail + cd gh-pages + + # 1. Replace target subdir + rm -rf "$TARGET" + mkdir -p "$TARGET" + cp -r ../site/. "$TARGET/" + + # 2. On release: also refresh latest/ and append to versions.json + if [ "$IS_RELEASE" = "true" ]; then + rm -rf latest + mkdir -p latest + cp -r ../site/. latest/ + + if [ ! -f versions.json ]; then + echo "[]" > versions.json + fi + jq --arg target "$TARGET" --arg tag "$TAG" \ + 'map(select(.version != $target)) | [{"version": $target, "title": $tag, "aliases": []}] + .' \ + versions.json > versions.json.new + mv versions.json.new versions.json + fi + + # 3. Ensure root redirect (latest > main) + if [ -d "latest" ]; then + REDIRECT_TARGET="latest/" + else + REDIRECT_TARGET="main/" + fi + cat > index.html < + + + + Compendium documentation + + + + +

Redirecting to ./${REDIRECT_TARGET}

+ + + HTML + + # 4. Initial files + [ -f versions.json ] || echo "[]" > versions.json + touch .nojekyll + + - name: Commit and push gh-pages + run: | + set -euo pipefail + cd gh-pages + git add -A + if git diff --cached --quiet; then + echo "No changes to publish." + exit 0 + fi + git commit -m "docs: deploy ${{ needs.build.outputs.version_path }} from ${{ github.sha }}" + git push origin gh-pages diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..270b107 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,4 @@ +_site/ +api/ +obj/ +.docfx/ diff --git a/docs/adapters/aspnetcore.md b/docs/adapters/aspnetcore.md new file mode 100644 index 0000000..f56a356 --- /dev/null +++ b/docs/adapters/aspnetcore.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.AspNetCore + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +ASP.NET Core integration: tenant validation middleware, problem details, auth helpers. diff --git a/docs/adapters/lemonsqueezy.md b/docs/adapters/lemonsqueezy.md new file mode 100644 index 0000000..7f97782 --- /dev/null +++ b/docs/adapters/lemonsqueezy.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.LemonSqueezy + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +LemonSqueezy billing adapter. diff --git a/docs/adapters/listmonk.md b/docs/adapters/listmonk.md new file mode 100644 index 0000000..fe3e62b --- /dev/null +++ b/docs/adapters/listmonk.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.Listmonk + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +Listmonk newsletter adapter. diff --git a/docs/adapters/openrouter.md b/docs/adapters/openrouter.md new file mode 100644 index 0000000..04f1411 --- /dev/null +++ b/docs/adapters/openrouter.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.OpenRouter + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +OpenRouter AI provider adapter. diff --git a/docs/adapters/postgresql.md b/docs/adapters/postgresql.md new file mode 100644 index 0000000..bafa037 --- /dev/null +++ b/docs/adapters/postgresql.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.PostgreSQL + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +PostgreSQL event store and projection store. diff --git a/docs/adapters/redis.md b/docs/adapters/redis.md new file mode 100644 index 0000000..29bff4e --- /dev/null +++ b/docs/adapters/redis.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.Redis + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +Redis cache adapter. diff --git a/docs/adapters/stripe.md b/docs/adapters/stripe.md new file mode 100644 index 0000000..1ccbe99 --- /dev/null +++ b/docs/adapters/stripe.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.Stripe + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +Stripe billing adapter. diff --git a/docs/adapters/toc.yml b/docs/adapters/toc.yml new file mode 100644 index 0000000..73221e5 --- /dev/null +++ b/docs/adapters/toc.yml @@ -0,0 +1,16 @@ +- name: ASP.NET Core + href: aspnetcore.md +- name: PostgreSQL + href: postgresql.md +- name: Redis + href: redis.md +- name: Zitadel + href: zitadel.md +- name: Stripe + href: stripe.md +- name: LemonSqueezy + href: lemonsqueezy.md +- name: Listmonk + href: listmonk.md +- name: OpenRouter + href: openrouter.md diff --git a/docs/adapters/zitadel.md b/docs/adapters/zitadel.md new file mode 100644 index 0000000..65acb9e --- /dev/null +++ b/docs/adapters/zitadel.md @@ -0,0 +1,5 @@ +# Compendium.Adapters.Zitadel + +> Coming soon — tracked in [POM-184](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-184). + +Zitadel OIDC identity adapter. diff --git a/docs/adr/toc.yml b/docs/adr/toc.yml new file mode 100644 index 0000000..080ef3c --- /dev/null +++ b/docs/adr/toc.yml @@ -0,0 +1,14 @@ +- name: Index + href: README.md +- name: 0001 — Result pattern + href: 0001-result-pattern.md +- name: 0002 — Hexagonal architecture + href: 0002-hexagonal-architecture.md +- name: 0003 — Zero-dependency Core + href: 0003-zero-dep-core.md +- name: 0004 — Multi-tenancy strategy + href: 0004-multi-tenancy-strategy.md +- name: 0005 — Event sourcing + href: 0005-event-sourcing-vs-state.md +- name: Template + href: 0000-template.md diff --git a/docs/concepts/event-sourcing.md b/docs/concepts/event-sourcing.md new file mode 100644 index 0000000..d00bd3f --- /dev/null +++ b/docs/concepts/event-sourcing.md @@ -0,0 +1,5 @@ +# Event Sourcing + +> Coming soon — tracked in [POM-183](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-183). + +Why event sourcing, append-only stores, aggregates vs projections, and snapshots in Compendium. See also [ADR 0005](../adr/0005-event-sourcing-vs-state.md). diff --git a/docs/concepts/hexagonal-architecture.md b/docs/concepts/hexagonal-architecture.md new file mode 100644 index 0000000..4b29163 --- /dev/null +++ b/docs/concepts/hexagonal-architecture.md @@ -0,0 +1,5 @@ +# Hexagonal Architecture + +> Coming soon — tracked in [POM-183](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-183). + +Core / Application / Adapters, ports vs adapters, why Compendium keeps Core dependency-free. See also [ADR 0002](../adr/0002-hexagonal-architecture.md) and [ADR 0003](../adr/0003-zero-dep-core.md). diff --git a/docs/concepts/multi-tenancy.md b/docs/concepts/multi-tenancy.md new file mode 100644 index 0000000..bedf6ea --- /dev/null +++ b/docs/concepts/multi-tenancy.md @@ -0,0 +1,5 @@ +# Multi-tenancy + +> Coming soon — tracked in [POM-183](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-183). + +`TenantContext`, multi-source resolution (header / subdomain / JWT), and consistency validation. See also [ADR 0004](../adr/0004-multi-tenancy-strategy.md). diff --git a/docs/concepts/result-pattern.md b/docs/concepts/result-pattern.md new file mode 100644 index 0000000..b60a868 --- /dev/null +++ b/docs/concepts/result-pattern.md @@ -0,0 +1,5 @@ +# Result Pattern + +> Coming soon — tracked in [POM-183](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-183). + +`Result`, `Error`, and why Compendium does not use exceptions for control flow. See also [ADR 0001](../adr/0001-result-pattern.md). diff --git a/docs/concepts/toc.yml b/docs/concepts/toc.yml new file mode 100644 index 0000000..8aaf73d --- /dev/null +++ b/docs/concepts/toc.yml @@ -0,0 +1,8 @@ +- name: Event Sourcing + href: event-sourcing.md +- name: Hexagonal Architecture + href: hexagonal-architecture.md +- name: Result Pattern + href: result-pattern.md +- name: Multi-tenancy + href: multi-tenancy.md diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..e3b74b2 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "files": ["**/*.csproj"], + "src": "../src", + "exclude": ["**/Compendium.Adapters.Shared.csproj"] + } + ], + "dest": "api", + "includePrivateMembers": false, + "disableGitFeatures": false, + "disableDefaultFilter": false, + "noRestore": false, + "namespaceLayout": "flattened", + "memberLayout": "samePage", + "EnumSortOrder": "alphabetic", + "allowCompilationErrors": false + } + ], + "build": { + "content": [ + { + "files": ["**/*.{md,yml}"], + "exclude": ["_site/**", "obj/**"] + } + ], + "resource": [ + { + "files": ["images/**"] + } + ], + "output": "_site", + "globalMetadata": { + "_appName": "Compendium", + "_appTitle": "Compendium — .NET event-sourcing framework", + "_enableSearch": true, + "_appFooter": "Copyright © 2026 Sassy Solutions. Released under the MIT License. View on GitHub.", + "_appLogoPath": "images/logo.svg", + "_appFaviconPath": "images/favicon.ico", + "_gitContribute": { + "repo": "https://github.com/sassy-solutions/compendium", + "branch": "main" + }, + "_disableContribution": false + }, + "template": ["default", "modern"], + "postProcessors": ["ExtractSearchIndex"], + "keepFileLink": false, + "disableGitFeatures": false, + "sitemap": { + "baseUrl": "https://sassy-solutions.github.io/compendium/", + "priority": 0.5, + "changefreq": "weekly" + } + } +} diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..a8d3b83 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,13 @@ +# Getting Started + +> Coming soon — tracked in [POM-182](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+POM-182). + +This page will walk you through: + +1. Installing the Compendium NuGet packages +2. Defining your first event-sourced aggregate +3. Wiring CQRS dispatchers in `Program.cs` +4. Dispatching a command and reading a projection +5. Running one of the [samples](https://github.com/sassy-solutions/compendium/tree/main/samples) + +In the meantime, the [README](https://github.com/sassy-solutions/compendium/blob/main/README.md) has a Quick Start snippet. diff --git a/docs/images/.gitkeep b/docs/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..3c191b5 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,35 @@ +--- +_layout: landing +title: Compendium +description: A pragmatic .NET framework for building event-sourced, multi-tenant SaaS applications. +--- + +# Compendium + +> A pragmatic .NET framework for building event-sourced, multi-tenant SaaS applications. + +Compendium distills years of building event-sourced SaaS into a small set of focused .NET 9 packages: DDD primitives, CQRS handlers, an event store, multi-tenancy, and ready-to-use adapters for PostgreSQL, Redis, Zitadel, Stripe, LemonSqueezy, Listmonk, OpenRouter, and ASP.NET Core. + +## Why Compendium? + +- **Zero-dependency Core** — Pure DDD primitives (`AggregateRoot`, `ValueObject`, `Result`, `Error`) with no dependencies beyond the .NET BCL. See [ADR 0003](adr/0003-zero-dep-core.md). +- **CQRS + Event Sourcing built-in** — Command/query dispatchers, event store interfaces, and a PostgreSQL adapter wired out of the box. See [ADR 0005](adr/0005-event-sourcing-vs-state.md). +- **Multi-tenancy native** — Tenant context, resolution, and scoping baked into the primitives. See [ADR 0004](adr/0004-multi-tenancy-strategy.md). +- **Result pattern everywhere** — No control-flow exceptions. Every fallible operation returns `Result`. See [ADR 0001](adr/0001-result-pattern.md). +- **Hexagonal architecture, strict** — Ports and adapters, with a clear separation between Core, Application, and Adapters. See [ADR 0002](adr/0002-hexagonal-architecture.md). +- **Modular adapters** — Pick only what you need. + +## Where to start + +- New to Compendium? → [Getting Started](getting-started.md) +- Want to understand the design? → [Concepts](concepts/event-sourcing.md) and [Architecture Decisions](adr/README.md) +- Looking for a specific adapter? → [Adapters](adapters/aspnetcore.md) +- Wondering what's next? → [Roadmap](roadmap.md) + +## Status + +Compendium is at **`v1.0.0-preview.1`**. APIs in `Compendium.Core` and `Compendium.Abstractions.*` are intended to be stable; adapter APIs may evolve based on production feedback. See [CHANGELOG](https://github.com/sassy-solutions/compendium/blob/main/CHANGELOG.md) for what has shipped. + +## License + +MIT © 2026 Sassy Solutions. See [LICENSE](https://github.com/sassy-solutions/compendium/blob/main/LICENSE). diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..3c6d542 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,12 @@ +--- +uid: roadmap +title: Roadmap +--- + +# Roadmap + +The canonical, living roadmap is at [`ROADMAP.md`](https://github.com/sassy-solutions/compendium/blob/main/ROADMAP.md) in the repository root, alongside the source. It captures vision, themes, what's next, and what is intentionally out of scope. + +A copy is rendered below at build time when available; otherwise visit GitHub for the latest. + +> If you'd like to influence the roadmap, please vote (👍) on issues tagged [`roadmap-input`](https://github.com/sassy-solutions/compendium/issues?q=is%3Aissue+label%3Aroadmap-input) or open a [Discussion](https://github.com/sassy-solutions/compendium/discussions). diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..53ee015 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,14 @@ +- name: Home + href: index.md +- name: Getting Started + href: getting-started.md +- name: Concepts + href: concepts/ +- name: Adapters + href: adapters/ +- name: Architecture Decisions + href: adr/ +- name: Roadmap + href: roadmap.md +- name: API Reference + href: api/