A shared anime tracking platform for pairs and small groups. BloomWatch lets friends maintain a joint backlog, log watch history, leave separate ratings, and surface compatibility analytics -- without relying on spreadsheets or Discord threads.
Backend
- .NET 10 / ASP.NET Core (Minimal APIs)
- PostgreSQL -- one schema per module
- Entity Framework Core 9 + Npgsql
- JWT HS256 (1-hour expiry) + BCrypt password hashing (work factor 12)
- OpenAPI + Scalar interactive API docs
- DDD-inspired modular monolith architecture
Frontend
- Angular 21 with standalone components and signal-based APIs
- SCSS design system with CSS custom property tokens
- Kawaii/Y2K visual theme (Quicksand + Nunito typefaces, gel effects, pastel palette)
- Vitest for unit testing
Testing
- xUnit, NSubstitute, FluentAssertions (backend)
- 72 automated tests across 7 test projects
src/
├── BloomWatch.Api/ # HTTP host -- entry point, middleware, endpoint registration
├── BloomWatch.SharedKernel/ # Shared abstractions used across modules
├── BloomWatch.UI/ # Angular 21 frontend application
│ └── src/
│ ├── app/
│ │ ├── core/layout/ # Shell layout (nav bar) and minimal layout (auth pages)
│ │ ├── features/ # Feature modules: auth, dashboard, watch-spaces, settings, showcase
│ │ └── shared/
│ │ ├── styles/ # Design tokens, base styles, animations, utilities
│ │ └── ui/ # Bloom component library (button, card, input, badge, avatar)
│ └── styles.scss # Global stylesheet
└── Modules/
├── Identity/
│ ├── BloomWatch.Modules.Identity.Domain/ # Aggregates, value objects, repository interfaces
│ ├── BloomWatch.Modules.Identity.Application/ # Use case handlers, service abstractions
│ ├── BloomWatch.Modules.Identity.Infrastructure/ # EF Core, BCrypt, JWT, migrations
│ └── BloomWatch.Modules.Identity.Contracts/ # Integration events for inter-module communication
├── WatchSpaces/
│ ├── BloomWatch.Modules.WatchSpaces.Domain/ # WatchSpace aggregate, Invitation entity, member rules
│ ├── BloomWatch.Modules.WatchSpaces.Application/ # 12 use case handlers (create, invite, accept, leave, ...)
│ ├── BloomWatch.Modules.WatchSpaces.Infrastructure/ # EF Core, email lookup, migrations
│ └── BloomWatch.Modules.WatchSpaces.Contracts/ # Integration events for downstream modules
└── AniListSync/
├── BloomWatch.Modules.AniListSync.Domain/ # (reserved for future domain entities)
├── BloomWatch.Modules.AniListSync.Application/ # Search query handler, client abstraction
├── BloomWatch.Modules.AniListSync.Infrastructure/ # AniList GraphQL client, in-memory caching
└── BloomWatch.Modules.AniListSync.Contracts/ # (reserved for integration events)
tests/
├── BloomWatch.Modules.Identity.UnitTests/ # Domain + use case unit tests (17 tests)
├── BloomWatch.Modules.Identity.IntegrationTests/ # HTTP endpoint integration tests (9 tests)
├── BloomWatch.Modules.WatchSpaces.UnitTests/ # Domain unit tests (23 tests)
├── BloomWatch.Modules.WatchSpaces.IntegrationTests/ # HTTP endpoint integration tests (10 tests)
├── BloomWatch.Modules.AniListSync.UnitTests/ # Handler + caching unit tests (6 tests)
└── BloomWatch.Modules.AniListSync.IntegrationTests/ # HTTP endpoint integration tests (7 tests)
docs/
├── architecture.md # Full system design, module breakdown, and planned feature phases
└── user-stories.md # Product user stories and completion tracking
openspec/ # Spec-driven change management (see below)
- .NET 10 SDK
- Node.js 18+ and npm 10+ (for the Angular frontend)
- PostgreSQL running locally (default:
localhost:5432)
git clone <repo-url>
cd bloomwatch
dotnet restoreThe default connection string in appsettings.json targets a local PostgreSQL instance:
Host=localhost;Database=bloomwatch;Username=postgres;Password=postgres
Override it in appsettings.Development.json or via environment variable if your setup differs:
export ConnectionStrings__DefaultConnection="Host=localhost;Database=bloomwatch;Username=youruser;Password=yourpass"dotnet ef database update --project src/Modules/Identity/BloomWatch.Modules.Identity.Infrastructure \
--startup-project src/BloomWatch.Api
dotnet ef database update --project src/Modules/WatchSpaces/BloomWatch.Modules.WatchSpaces.Infrastructure \
--startup-project src/BloomWatch.Apidotnet run --project src/BloomWatch.ApiThe API is available at http://localhost:5192 (or https://localhost:7073).
In development mode, interactive API documentation is served by Scalar at /scalar/v1.
cd src/BloomWatch.UI
npm install
npm startThe Angular dev server starts at http://localhost:4200.
When running in Development mode, visit /scalar/v1 for a full interactive API reference powered by Scalar and the OpenAPI spec.
POST /auth/register # Create a new user account
POST /auth/login # Authenticate and receive a JWT
GET /users/me # Get the authenticated user's profileAll WatchSpace endpoints require a valid JWT (Authorization: Bearer <token>).
POST /watchspaces # Create a new watch space
GET /watchspaces # List spaces you belong to
GET /watchspaces/{id} # Get a single space (members only)
PATCH /watchspaces/{id} # Rename a space (owner only)
POST /watchspaces/{id}/transfer-ownership # Transfer ownership to a member
POST /watchspaces/{id}/invitations # Invite a user by email (owner only)
GET /watchspaces/{id}/invitations # List pending invitations (owner only)
DELETE /watchspaces/{id}/invitations/{invitationId} # Revoke an invitation (owner only)
POST /watchspaces/invitations/{token}/accept # Accept an invitation by token
POST /watchspaces/invitations/{token}/decline # Decline an invitation by token
DELETE /watchspaces/{id}/members/{userId} # Remove a member (owner only)
DELETE /watchspaces/{id}/members/me # Leave a spaceRequires a valid JWT. Results are cached in memory for 5 minutes.
GET /api/anilist/search?query=cowboy+bebop # Search for anime via AniListA .http file (src/BloomWatch.Api/BloomWatch.Api.http) is included for quick manual testing in VS Code or Rider.
The Angular frontend lives in src/BloomWatch.UI/ and provides the user-facing application shell.
| Route | Layout | Description |
|---|---|---|
/login |
Minimal | Login page (no nav bar) |
/register |
Minimal | Registration page (no nav bar) |
/ |
Shell | Dashboard |
/watch-spaces |
Shell | Watch space list and detail views |
/settings |
Shell | User settings |
/showcase |
Shell | Component library showcase |
The frontend includes a custom component library under src/app/shared/ui/, built with the kawaii/Y2K design system. All components use the bloom- selector prefix and follow Angular standalone component patterns with signal-based inputs and outputs.
| Component | Selector | Description |
|---|---|---|
| Button | bloom-button |
Primary, secondary, ghost, and danger variants with gel shine effects |
| Card | bloom-card |
Content container with multiple visual variants |
| Input | bloom-input |
Form input with label, validation states, and size options |
| Badge | bloom-badge |
Status and category badges with color options |
| Avatar | bloom-avatar |
User avatar with status indicators |
| Avatar Stack | bloom-avatar-stack |
Overlapping avatar group display |
The design system is defined through SCSS partials in src/app/shared/styles/:
_tokens.scss-- CSS custom property design tokens (colors, typography, spacing, shadows, radii, animation timings)_base.scss-- Global resets and base element styles_animations.scss-- Keyframe animations (sparkle, float, bounce, shimmer, gel press effects)_utilities.scss-- Utility classes for common patterns
All configuration lives in appsettings.json. The key sections:
{
"ConnectionStrings": {
"DefaultConnection": "<postgres connection string>"
},
"Identity": {
"Jwt": {
"SecretKey": "<long random secret -- change before deploying>",
"Issuer": "BloomWatch",
"Audience": "BloomWatch"
}
}
}# All backend tests
dotnet test
# By module
dotnet test tests/BloomWatch.Modules.Identity.UnitTests
dotnet test tests/BloomWatch.Modules.Identity.IntegrationTests
dotnet test tests/BloomWatch.Modules.WatchSpaces.UnitTests
dotnet test tests/BloomWatch.Modules.WatchSpaces.IntegrationTests
dotnet test tests/BloomWatch.Modules.AniListSync.UnitTests
dotnet test tests/BloomWatch.Modules.AniListSync.IntegrationTests
# Frontend tests
cd src/BloomWatch.UI && npm testBackend integration tests use an in-memory SQLite database via WebApplicationFactory -- no running database required.
BloomWatch uses OpenSpec -- a spec-driven change management workflow -- to plan, document, and track every meaningful feature change before and during implementation.
Each change moves through three stages:
propose -> apply -> archive
| Stage | What happens |
|---|---|
| propose | A change is described with a proposal (why/what/impact), a design doc, feature specs, and a task list |
| apply | Tasks are worked through one at a time, guided by the specs |
| archive | The completed change is moved to openspec/changes/archive/ for historical reference |
openspec/
├── config.yaml # OpenSpec configuration
├── specs/ # Standalone feature specs (reusable across changes)
│ ├── user-registration/
│ ├── user-authentication/
│ ├── user-profile/
│ ├── watch-space-management/
│ ├── watch-space-invitations/
│ ├── watch-space-membership/
│ ├── anilist-search/
│ └── angular-routing-shell/
└── changes/
└── archive/ # Completed changes
| Change | Date | Summary |
|---|---|---|
scaffold-identity-module |
2026-03-13 | User registration, JWT login, full Identity module (domain to API) |
watchspaces-module |
2026-03-13 | Watch space creation, email invitations, membership management |
user-profile-endpoint |
2026-03-13 | GET /users/me authenticated profile retrieval |
anilist-search-proxy |
2026-03-16 | GET /api/anilist/search with GraphQL proxy and in-memory caching |
angular-project-setup-routing-shell |
2026-03-16 | Angular 21 frontend scaffold, routing shell, kawaii/Y2K design system, component library |
The workflow is integrated with Claude Code via slash commands:
/opsx:propose -- describe a new feature and generate proposal + specs + tasks
/opsx:explore -- think through requirements or investigate an existing change
/opsx:apply -- implement tasks from an active change
/opsx:archive -- finalize and archive a completed change
See docs/architecture.md for the full system design, including:
- Modular monolith structure and module boundaries
- DDD layer responsibilities (Domain / Application / Infrastructure / Contracts)
- Database schema strategy (one PostgreSQL schema per module)
- Planned feature phases (joint backlog, AniList integration, compatibility analytics)