Exec Magica API is the backend and web client for the collectible card game EXEC_MAGICA. This repository is a production-deployed ASP.NET Core service — role-based auth, a card catalog with server-side filtering, per-user deck building, and server-side SVG card rendering — paired with a Blazor WebAssembly frontend, in a clean layered architecture.
The app is deployed and running — browse the catalog, register an account and build decks, no setup required:
| 🌐 Web app | exec-magica.kuzmenkoff.dev |
| 🧪 API playground (Scalar) | /scalar/v1 |
| 📚 API reference (DocFX) | kuzmenkoff.github.io/exec_magica-api |
- ▶ Live app
- About
- Roles & use cases
- Architecture
- Data model
- Getting started
- Project structure
- Tech stack
- API
- Testing
- Documentation
- Roadmap
- License
Exec Magica API is the server side and web client of the collectible card game EXEC_MAGICA — a standalone, production-grade ASP.NET Core application built on top of the game and its card data. It manages the card database, player accounts and decks, and renders cards on the server.
The goal was a clean, real-world .NET backend end-to-end: layered (Clean) architecture, a real relational database with migrations, token auth with roles, automated tests and CI, and a one-command Docker deployment — fronted by a Blazor WebAssembly UI so the whole thing is usable in the browser.
- 🧱 Clean/layered architecture — Domain · Application · Infrastructure · Api, dependencies point inward
- 🔐 JWT auth with roles — ASP.NET Identity; Admin (card CRUD) vs User (browse + own decks)
- 🃏 Server-side SVG card rendering —
GET /api/cards/{id}/rendercomposites frame + art + DB stats - 🔎 Server-side catalog filtering — one composable EF Core query (class · mana · keywords · stats · sort)
- 🗃 PostgreSQL + EF Core — real migrations, relations and primitive collections (not in-memory)
- 🖥 Blazor WebAssembly client — catalog, auth, deck editor and admin, served single-origin by the API
- ✅ Tested + CI — xUnit unit tests · GitHub Actions build+test
- 🐳 One-command deploy — Docker Compose (API + Postgres), live behind Caddy with automatic HTTPS
- Guest — browse and filter the catalog, view rendered cards, register, log in.
- User — everything a guest can do, plus build and manage their own decks.
- Admin — everything a user can do, plus full card CRUD.
A Clean/layered architecture across five projects under src/, plus a test project.
Dependencies point inward — Domain references nothing; the database, web framework
and UI live at the edges (Infrastructure, Api, Client). The graph below shows the projects and how they depend on one another.
flowchart TD
CLI["ExecMagica.Client<br/><i>Blazor WASM — catalog · decks · admin</i>"]
API["ExecMagica.Api<br/><i>controllers · DI root · JWT auth · hosts the client</i>"]
INF["ExecMagica.Infrastructure<br/><i>EF Core repos · Identity · JWT · SVG renderer</i>"]
APP["ExecMagica.Application<br/><i>services · interfaces · DTOs · CardQuery / ApplyQuery</i>"]
DOM["ExecMagica.Domain<br/><i>entities · enums · DeckRules</i>"]
TST["tests/ExecMagica.Tests<br/><i>xUnit</i>"]
API --> CLI
API --> INF
INF --> APP
APP --> DOM
TST --> APP
INF -. Npgsql .-> DB[(PostgreSQL)]
CLI -. HTTPS · Caddy .-> API
Solid arrows are compile-time dependencies; dotted arrows are runtime connections.
ExecMagica.Domain— the core model of what the game is. Holds the entities (Card,CardEffect,Deck,DeckCard), the enums (CardClass,KeywordType, effect triggers/types/targets) and the invariants inDeckRules(max copies per card, deck size, decks per user). It references nothing — no EF Core, no ASP.NET — so the business concepts stay framework-free and every other layer is built on top of them.ExecMagica.Application— what the app does. Contains the use-case services (CardService,DeckService), the interfaces the core needs (ICardRepository,IDeckRepository, …) without knowing how they're fulfilled, the DTOs that shape the API contract, and the composableCardQuery/ApplyQuerycatalog filter. Depends only onDomain, which keeps the application logic independent of the database and the web stack and makes it unit-testable. (ApplyQueryis deliberately EF-agnostic — plain LINQ overIQueryable<Card>, so it both translates to SQL via Npgsql and runs in-memory in tests.)ExecMagica.Infrastructure— how the abstractions are actually fulfilled, against real technology. Provides EF Core (AppDbContext, repositories, migrations, seeding), ASP.NET Identity (users & roles), JWT token generation, and the SVG card renderer that composites the frame, artwork and DB stats. It implements theApplicationinterfaces and depends onApplication+Domain, so all the external-tech detail lives at the edge and never leaks into the core.ExecMagica.Api— the HTTP entry point and composition root. Exposes the controllers (Cards,Decks,Auth,Rules), wires theApplicationinterfaces to theirInfrastructureimplementations through DI, runs the auth middleware (JWT validation + roles), and hosts the Blazor client single-origin (serves the WASM/static files with anindex.htmlfallback). Depends onApplication,InfrastructureandClient— it is the one place where the outside world (HTTP, DI, hosting) meets the core.ExecMagica.Client— the user-facing Blazor WebAssembly single-page app: the pages (catalog, deck editor, admin, login/register), typed API clients, JWT auth state and the themed card view. It has no project references — it talks to the backend purely over the HTTP/JSON contract, so the frontend stays a fully independent app that could be replaced by any other client.tests/ExecMagica.Tests— xUnit unit tests over the core logic (catalog filtering/sorting viaApplyQuery, DTO mapping, deck rules). ReferencesApplication(Domainflows transitively) and nothing heavier, so the business rules are verified fast and without a database or the ASP.NET/EF stack.
PostgreSQL via EF Core. Game tables (Cards, CardEffects, Decks, DeckCards) alongside
the ASP.NET Identity tables (users & roles). Keywords are stored as a Postgres array column
on Cards rather than a join table.
Cards— one row per card definition;Keywordsis a Postgresenum[]array column.Cards→CardEffects— one-to-many (cascade delete). The effects table backs a future effect system and is currently unpopulated (cards carry keywords + rules text only).Decks→DeckCards→Cards— a deck holds many entries; each entry links one card with aQuantity. The(DeckId, CardId)pair is the composite key; copies/size are bounded byDeckRules.AspNetUsers→Decks— a user owns many decks (cascade delete when the user is removed).AspNetUsers↔AspNetRolesviaAspNetUserRoles— theAdmin/Userroles that gate card CRUD vs deck management.
You may run the app locally to try or study it. Deploying, modifying or redistributing it is not permitted — see LICENSE.
Prerequisites: Docker with Docker Compose. (Optionally the .NET 10 SDK if you want to run the API or the tests outside a container.)
-
Clone and configure
git clone https://github.com/kuzmenkoff/exec_magica-api.git cd exec_magica-api cp .env.example .envEdit
.env:POSTGRES_PASSWORD— any value.JWT_KEY— a long random secret (32+ chars).SEED_ADMIN_EMAIL/SEED_ADMIN_PASSWORD— the admin account created on first run. The password must meet ASP.NET Identity rules: ≥6 chars with an uppercase letter, a lowercase letter and a digit (e.g.Admin1234).
-
Run
docker compose up -d --build
Postgres starts first; the API then applies EF Core migrations and seeds the card catalog and the admin account on startup.
-
Open
- Web app — http://localhost:8080
- API playground (Scalar) — http://localhost:8080/scalar/v1
- Sign in with the admin from your
.env, or register a normal account.
Run the tests
dotnet testCard frames: the renderer expects
card_back.png,entity_front.pngandspell_front.pnginsrc/ExecMagica.Api/wwwroot/cards/. Those templates derive from third-party assets and are not included (see THIRD-PARTY-NOTICES.md) — supply your own for framed cards. Rendering still works without them, just unframed.
src/
├── ExecMagica.Domain/ # core model — no dependencies
│ ├── Entities/ # Card · CardEffect · Deck · DeckCard
│ └── Enums/ # CardClass · KeywordType · effect triggers/types/targets
├── ExecMagica.Application/ # use cases — depends only on Domain
│ ├── Dtos/ # API-facing DTOs + CardQuery
│ ├── Extensions/ # ApplyQuery — composable, EF-agnostic catalog filter
│ ├── Interfaces/ # repository/service abstractions
│ └── Services/ # CardService · DeckService
├── ExecMagica.Infrastructure/ # implementations against real technology
│ ├── Data/ # AppDbContext · migrations · seeding
│ ├── Identity/ # ASP.NET Identity user · JWT token generator
│ ├── Rendering/ # SvgCardRenderService
│ └── Repositories/ # EF Core repositories
├── ExecMagica.Api/ # HTTP entry point + DI composition root; hosts the client
│ ├── Controllers/ # Cards · Decks · Auth · Rules
│ ├── art/ # card artwork (AI-generated), served at /art
│ └── wwwroot/ # fonts · card frame templates (not in repo) · served client
└── ExecMagica.Client/ # Blazor WebAssembly UI — no project references
├── Pages/ # Home · DeckEditor · MyDecks · AdminCards · Login/Register
├── Components/ # CardView · CardFilters
├── Services/ # typed API clients
├── Auth/ # JWT auth state + handlers
└── wwwroot/ # app.css · js · bootstrap
tests/ExecMagica.Tests/ # xUnit unit tests — ApplyQuery · mapping · deck rules
docs/ # README assets
Dockerfile · docker-compose.yml · docker-compose.prod.yml # image + dev / prod stacks
docfx.json · index.md · toc.yml # DocFX config (API reference)
LICENSE · THIRD-PARTY-NOTICES.md
The layered split maps directly to the projects under src/: Domain (pure model) →
Application (use cases + interfaces) → Infrastructure (EF Core, Identity, rendering) →
Api (HTTP + composition root), with Client as a standalone Blazor WASM frontend the API
hosts. Tests target the core (Application/Domain) only.
| Area | Tech |
|---|---|
| Backend / language | ASP.NET Core (.NET 10) · C# |
| Architecture | Clean/layered — Domain · Application · Infrastructure · Api |
| Database / ORM | PostgreSQL 16 · EF Core (Npgsql) · migrations |
| Auth | ASP.NET Core Identity · JWT (Bearer) · Admin/User roles |
| Frontend | Blazor WebAssembly · scoped CSS |
| Card rendering | Server-side SVG compositing (frame + artwork + Peaberry font) |
| API docs | OpenAPI · Scalar (playground) · DocFX (XML docs → GitHub Pages) |
| Testing | xUnit · Moq |
| CI/CD | GitHub Actions — build + test, DocFX deploy to Pages |
| Deployment | Docker · Docker Compose · Caddy (reverse proxy, automatic HTTPS) |
Interactive docs are generated from OpenAPI and served by Scalar at
/scalar/v1.
Authentication is a Bearer JWT obtained from POST /api/auth/login.
| Method | Route | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
— | Create a user account |
POST |
/api/auth/login |
— | Log in — returns a JWT |
GET |
/api/cards |
— | List / filter the catalog (search · class · mana · keywords · stats · sort) |
GET |
/api/cards/{id} |
— | Get one card |
GET |
/api/cards/{id}/render |
— | Render the card as SVG |
POST |
/api/cards |
Admin | Create a card |
PUT |
/api/cards/{id} |
Admin | Update a card |
DELETE |
/api/cards/{id} |
Admin | Delete a card |
GET |
/api/rules |
— | Deck-building limits (copies · size · decks per user) |
GET |
/api/decks |
User | List the current user's decks |
GET |
/api/decks/{id} |
User | Get one of the user's decks |
POST |
/api/decks |
User | Create a deck |
PUT |
/api/decks/{id} |
User | Rename a deck |
DELETE |
/api/decks/{id} |
User | Delete a deck |
POST |
/api/decks/{deckId}/cards |
User | Add a card to a deck |
DELETE |
/api/decks/{deckId}/cards/{cardId} |
User | Remove a card from a deck |
xUnit unit tests over the core logic — fast and database-free (mocked repositories and
in-memory IQueryable), so they run in CI on every push.
| Suite | Tests | Covers |
|---|---|---|
CardQueryTests |
11 | ApplyQuery filters (collectible · search · class · mana incl. 7+ · keywords · attack/health ranges) and sort (field + direction) |
CardServiceTests |
4 | Domain→DTO mapping and card lookups over a mocked repository |
DeckServiceTests |
6 | Deck operations and DeckRules (copy limit · deck size · decks per user) |
Run the full suite:
dotnet test📖 API reference — auto-generated from the code's XML doc comments (DocFX), redeployed to GitHub Pages on every push.
- OpenAPI / Scalar playground — try the endpoints live.
- EXEC_MAGICA Engine — the game engine and AI research framework this backend is built on (separate repo).
- Card effect system — populate the
CardEffectstable and execute effects at runtime (the data model is already in place). - Server-authoritative multiplayer — real-time matches over SignalR, reusing this database and domain.
- Admin: effect editor & artwork upload — extend the card admin form beyond the current base fields.
Source code is released under the PolyForm Strict License 1.0.0 — see LICENSE. You may view and study the code, but distributing, modifying or deploying it is not permitted.
Bundled third-party components (the Peaberry font and Bootstrap) remain under their own licenses — see THIRD-PARTY-NOTICES.md. The card artwork is AI-generated; the card frame templates derive from third-party Unity assets and are not included in this repository.
