Skip to content

KaliCZ/StrongTypes-Example

Repository files navigation

StrongTypes Example — Product Reviews

A complete, runnable showcase for Kalicz.StrongTypes: a product-reviews platform where every constrained value is a strong type — from the request DTO through the domain and the database, out into the OpenAPI document, and all the way into the generated TypeScript client. No data annotations, no guard clauses, no hand-written frontend types.

It doubles as a starter template for full-stack .NET web apps: ASP.NET Core (controllers) + EF Core/PostgreSQL + .NET Aspire + Zitadel auth + Vue 3, with spec-driven docs, ADRs, and a no-mocks test pyramid. The conventions are documented in CLAUDE.md and docs/.

Quickstart

Prerequisites:

One command:

dotnet run --project src/ProductReviews.AppHost

(or aspire run if you have the Aspire CLI).

The AppHost starts PostgreSQL, Zitadel (identity provider + hosted login), the API, and the frontend; installs frontend dependencies on first run; applies migrations; and seeds a browsable catalog. First start pulls container images — give it a few minutes; subsequent starts are fast.

Where to click:

  • The app: http://localhost:5173 — browse the catalog, open a product, read reviews.
  • Sign in to write reviews and vote: demo@productreviews.local / ProductReviews123! (a second account, critic@productreviews.local, same password, lets you vote on the first account's review — you can never vote on your own).
  • Swagger UI: http://localhost:5173/swagger — look at SubmitReviewRequest: rating carries minimum: 1, maximum: 5, title carries minLength: 1, email on the profile carries format: email. Nothing in the codebase states those rules except the types themselves.
  • Aspire dashboard — the URL is printed on startup; logs, traces, and resource states live there.

What to look at (the StrongTypes tour)

The point Where
DTOs with strong types, zero annotations ReviewContracts.cs
Maybe<T> three-state PATCH (omit = keep, {} = clear, {"Value": …} = set) EditReviewRequest, Review.ApplyEdit, EditReview.cs
Result<T, TError> + error enums instead of exceptions SubmitReview.cs → mapped exhaustively in ReviewsController.cs
Declaring your own strong type ([NumericWrapper]) Rating.cs
EF Core storing strong types directly (UseStrongTypes()) Persistence.cs, ReviewsDbContext.cs, the migration (plain varchar/int columns)
OpenAPI carrying the real constraints (AddStrongTypes()) OpenApi.cs, snapshot in frontend/openapi.json
Constraints flowing into TypeScript (generated client) schema.d.ts (generated), client.ts, editReviewBody.ts
Property-based tests with generated strong-typed values RatingTests.cs, ReviewEditTests.cs
The OpenAPI claims verified against the running API OpenApiDocumentTests.cs
Parse-don't-validate at the query boundary (Positive<int> paging, Rating[] filters) ReviewsController.cs, GetReviewsPage.cs

Tests

# Backend: domain unit + property tests, then wire-level integration tests
# (Testcontainers spins up a real PostgreSQL — Docker must be running)
dotnet test

# Frontend unit tests (Vitest)
npm --prefix frontend run test:unit

# End-to-end: builds the frontend, boots the WHOLE stack in E2E mode, and
# drives real browsing, sign-in, reviewing, and voting (Playwright)
npm --prefix frontend run test:e2e

Nothing is mocked anywhere — see ADR-0005.

The spec

The implementation follows the docs, not the other way around:

Links

About

Just example API that uses StrongTypes to showcase how nice and easy it can be.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors