Feature/2 add single project structure#11
Merged
Conversation
Move the existing multi-project template into a self-contained layered/ folder in preparation for a second 'modular' variant. Repo-level files (README, LICENSE, CONTRIBUTING, docs/, .github) stay at the root and are shared across variants. Behavior preserving: layered/ builds, unit tests pass, the template packs and instantiates with database/migrations intact. The shared-database hoist to root and the .github CI rework are intentionally deferred to later steps.
Second dotnet new variant 'clean-api-modular' alongside layered. Collapses Domain/Application/Infrastructure into one CleanApiStarter.Api project organized by folders (Domain/, Infrastructure/, Common/, Features/), reusing the AspNetCore and Configuration platform projects plus AppHost. Namespaces are CleanApiStarter.Api.*. Boundaries are enforced by NsDepCop (config.nsdepcop + WarningsAsErrors=NSDEPCOP01): Domain may not reference Features/Infrastructure, Features may not reference Infrastructure. Verified that an illegal Domain->Infrastructure reference fails the build with NSDEPCOP01. Both variants given distinct template identities/package ids: clean-api-layered and clean-api-modular. Verified: modular builds, unit + integration tests pass, and the template packs, instantiates, and the generated project builds. Deferred to step 3: shared database/ at repo root (modular currently has its own copy), .github CI matrix, CONTRIBUTING refresh, and a modular-specific README.
- Move database/ to the repo root as the single source of truth; each variant gets a git-ignored copy via scripts/sync-database.sh (CI and install scripts run it before building/packing). Removes the duplicated, drift-prone copies. - Rework CI as a matrix over [layered, modular]: build.yml, template.yml (packs + instantiates each variant by shortName/package), codeql.yml, release.yml (publishes both NuGet packages). dependabot.yml now watches both variant dirs. - Ship a per-variant generated-project build.yml (restores CI that generated projects lost when the repo .github moved to the root). - Fix per-variant install-template.sh for the new package ids + sync step. - Refresh CONTRIBUTING.md for the two-variant workflow; give modular its own README; update the root README (modular is available, database sync documented). Verified: both variants build, and both pack -> install -> instantiate -> the generated project builds, with migrations and a build workflow shipped.
Capture the architecture's evolution: seven-project split -> dual layered+modular variants -> a single modular solution of three projects with analyzer-enforced boundaries. Insights synthesized as general industry knowledge.
…uration into Api Per ADR-003, reduce the modular variant toward three projects: - Rename CleanApiStarter.AspNetCore -> CleanApiStarter.AspNetCoreDefaults (folder, csproj, assembly, namespace). - Fold CleanApiStarter.Configuration into the Api project (CleanApiStarter.Api. Configuration), removing the separate project. - Break the resulting reference cycle by keeping AspNetCoreDefaults application-agnostic: the JWT bearer token-validation options are now bound from AppSettings by the composition root (Api) via AddJwtBearerOptions, instead of inside the defaults project. Modular now has three source projects (Api, AspNetCoreDefaults, AppHost). Build, unit tests, and the Testcontainers integration test (which exercises JWT auth) all pass.
Consolidate to a single modular solution at the repository root, replacing the two-variant layout: - Delete the layered/ variant; move the modular solution (Api, AspNetCoreDefaults, AppHost + tests) to the root. - database/ at the root is referenced directly; remove the per-variant sync mechanism (scripts/sync-database.sh) and its gitignore entry. - Revert CI from a two-variant matrix to single-solution workflows; dependabot watches one nuget directory again. - Single template again: identity/shortName CleanApiStarter.Template / clean-api-starter, PackageId CleanApiStarter.Template. Exclude adr/, docs/, CONTRIBUTING from packed output. - Refresh README and CONTRIBUTING for the single-solution structure. Verified: build, format check, unit + integration tests pass; template packs, instantiates, and the generated three-project solution builds with migrations and CI workflows shipped.
- Delete docs/architecture/ardalis-clean-architecture-vsa-transcript.md. - Strip source attribution (author/transcript references) from clean-architecture-and-vertical-slices.md and reframe its now-stale two-variant wording for the single-solution structure. - Add an 'Architecture in brief' summary near the top of README.md that distills the three-decisions framing and links to the full architecture doc and the ADRs.
Config files under Solution Items now sit in nested .config/, .github/workflows/, and .template.config/ folders instead of a flat list. Fold the virtual src/Common group into src/ (the projects are siblings on disk) and normalize tests paths to forward slashes.
Folders-only step toward proper vertical slices: move the task contracts (ProjectTaskDto, Create/Update task validators) into Features/Projects/Tasks with a matching CleanApiStarter.Api.Features.Projects.Tasks namespace. Auth stays a flat capability folder. Handlers and endpoints are untouched.
…points/ folder Move the Auth/Projects (V1) and Projects (V2) endpoint groups and the dev Google login page into their feature folders with matching namespaces; remove the separate Endpoints/ folder. Endpoint groups are still discovered by reflection and continue to call ProjectService / IAuthService (handlers unchanged). Drop the now-unused Endpoints global using; refresh the README layout. Verified: build, format, unit and integration tests pass.
Retire the monolithic ProjectService, IProjectService, and the result enums. Each operation is now a self-contained slice co-locating its request record, validator, endpoint mapping, and handler that talks directly to IProjectRepository / IAuthService: Auth/ SignInWithGoogle, GetCurrentUser Projects/ CreateProject, GetProjects, GetProject, DeleteProject Projects/Tasks/ CreateTask, GetTasks, GetTask, UpdateTask, CompleteTask, DeleteTask Projects/V2/ GetProjects Thin per-feature IEndpointGroup classes (Auth, Projects, V2.Projects) just wire the route group and delegate to each slice's Map. Handlers return TypedResults directly, removing the DeleteProjectResult / ProjectTaskMutationResult indirection. Mapping moves to ProjectDto.From / ProjectTaskDto.From; a shared IUser.RequireId() guards the user id. IAuthService.SignInWithGoogleAsync now takes the id token string, dropping GoogleSignInDto. NsDepCop's Features-not-Infrastructure rule still holds (slices use interfaces; impls stay in Infrastructure). Unit test now covers the CompleteTask slice handler (asserts 409 + no save); Application.UnitTests gains a Microsoft.AspNetCore.App framework reference to assert on typed results. Build, format, unit + integration tests, and template pack/instantiate/build all pass.
Features is now one self-contained file per operation (endpoint + request + validator + handler); fix the composition-root bullet (no Endpoints folder).
Task-oriented recipes (add an endpoint/feature/migration) with copy-pasteable slice templates, instead of a descriptive auto-loaded AGENTS.md. Not an auto-load filename, so it costs no idle context tokens. Linked from CONTRIBUTING.
Add Claude Code project skills: - /add-endpoint — add an API operation as a vertical slice (path-scoped to Features/) - /add-migration — add a schema change + keep EF config in sync (path-scoped to database + persistence) Each is auto-loaded when relevant (paths frontmatter) or invokable directly. The skills ship inside generated projects; .claude/settings.local.json is git-ignored and excluded from the packed template. Remove the plain SKILLS.md doc (superseded); CONTRIBUTING now points at the skills.
The AddApplication extension did nothing but register validators after the slice refactor, so inline that single call into Program.cs and delete the file. Rename the infrastructure DI holder from the generic DependencyInjection to InfrastructureServiceCollectionExtensions (method AddInfrastructure unchanged).
Rename InfrastructureServiceCollectionExtensions -> InfrastructureExtensions and the generic AspNetCoreDefaults 'Extensions' partial -> AspNetCoreDefaultsExtensions (incl. the typeof reference in MapDefaultEndpoints). Extension method names are unchanged. Now every holder is <Area>Extensions: Infrastructure, AspNetCoreDefaults, WebApplication, OpenApiDocumentation, OptionsRegistration, User.
- CleanApiStarter.Application.UnitTests -> CleanApiStarter.UnitTests - CleanApiStarter.Api.IntegrationTests -> CleanApiStarter.IntegrationTests - CleanApiStarter.Tests (shared) -> CleanApiStarter.TestUtilities No more layer qualifier now that the app is a single project (matches the type-based convention: UnitTests / IntegrationTests / shared utilities). Updates csproj names, namespaces, project references, the .slnx, README, and CONTRIBUTING. The JWT issuer/audience string literals are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.