ASP.NET Core backend powering the Schuly ecosystem
Clean-architecture C# API serving the Schuly mobile app. Built on ASP.NET Core with CQRS via Mediator, EF Core on PostgreSQL, OIDC authentication, and an extensible plugin runtime.
src/Schuly.API- ASP.NET Core entry point + controllerssrc/Schuly.Application- CQRS commands/queries + handlerssrc/Schuly.Domain- entities (School,Class,Exam,Grade,Absence,AgendaEntry, ...)src/Schuly.Infrastructure- EF Core, OIDC, plugin hostsrc/Schuly.Tests- unit + integration tests
docker compose -f compose.dev.yml up -d # Postgres + SeaweedFS + SchulwareAPIcd src/Schuly.API
dotnet user-secrets set"ConnectionStrings:SchulyDatabase""Host=localhost;Port=2406;Database=schuly-dev;Username=postgres;Password=d4vpas8w0rd13!!!"
dotnet user-secrets set"Oidc:Authority""http://localhost:8080/realms/schuly"
dotnet run --urls=http://localhost:5033API reference (Scalar): http://localhost:5033/scalar · OpenAPI document: http://localhost:5033/openapi/v1.json
The connection string and Oidc:Authority are both required - the API stops on startup without the first, and the OpenAPI document fails without the second. Development setup walks through it properly.
Full documentation lives at docs.schuly.dev/SchulyBackend.
| Guide | What it covers |
|---|---|
| Development setup | Run the API and its dependencies locally, and the tests. |
| Configuration | Every setting: connection string, OIDC, S3 storage, avatar signing, logging. |
| Self-hosting | Stand up the full stack (Caddy, Keycloak, Postgres, SeaweedFS) from published images. |
| Production | Image tags, releases, and deployment notes. |
| Architecture | Projects, layering rules, request pipeline, document storage. |
| Plugin management | How plugins are declared, downloaded, and managed at runtime. |
| Migrations | Create and apply EF Core migrations. |
| Contributing | Workflow, branch and PR conventions. |
Which login systems the app offers is supplied by the loaded plugins, not baked into the backend: each plugin describes the system it serves via its IPluginLogin.SchoolSystem descriptor, and the backend seeds the catalog from those on load (seed-if-missing by Key, so admin edits survive). Install a plugin and its system appears in the picker with no operator config.
Operators can still add or override custom systems through a SchoolSystems config section, which seeds first and therefore wins on a matching Key.
See the plugin contract for the descriptor and a worked example.
| Repo | Purpose |
|---|---|
| Schuly | Flutter mobile app |
| SchulyBackend | ASP.NET Core API backend (this repo) |
| SchulyKeycloak | Keycloak image + the schuly realm |
| SchulyPluginAbstractions | Plugin contract (NuGet) |
| SchulyPlugins | Official plugins monorepo |
| SchulyWebsite | Landing site (schuly.dev) |
| SchulyDocs | Documentation site (docs.schuly.dev) |