docs(claude): add layered backend-architecture Claude Code skills#111
Merged
Conversation
Codifies the presentation-layer rule for Micronaut REST controllers: thin HTTP adapter only, no repository injection, no inline business logic, no OpenAPI annotations on the implementation class.
Codifies the application/business-logic layer: one @singleton service per feature package owns the repository and returns small outcome records/enums instead of the controller branching on business rules.
Full four-file sketch (Api interface, controller, service, outcome records) applying backend-controller-layer/backend-service-layer/ backend-openapi-contract together to PunishmentTemplateHandler.java, referenced from all three skills instead of duplicated across them.
Codifies moving @Operation/@ApiResponse/@Schema annotations off the controller implementation and onto a dedicated *Api interface the controller implements, which Micronaut/Swagger already supports.
Codifies the request/response DTO convention: a *RequestDTO with a nullable identifier (create vs. update) paired with a full *DTO for responses, both Serdeable/ReflectiveAccess records.
…ntroller-layer OneLiteFeatherNET/Otis's controllers show the same repository-in- controller and inline-business-logic smell, confirming this is a recurring org-wide pattern rather than a one-off in Blackhole. Adds a domain-neutral Widget* example alongside the PunishmentTemplate one.
…rvice-layer OneLiteFeatherNET/Otis has no service layer at all, confirming the missing-service gap is a recurring org-wide pattern rather than a one-off in Blackhole. Adds a domain-neutral WidgetService example alongside the PunishmentTemplateService one.
…enapi-contract OneLiteFeatherNET/Otis's controllers carry the same inline @Operation/@apiresponse bulk with no *Api interface, confirming this is a recurring org-wide pattern rather than a one-off in Blackhole. Adds a domain-neutral WidgetApi example alongside the PunishmentTemplateApi one.
…o-contract OneLiteFeatherNET/Otis's OtisPlayerDTO reuses one record for both request and response and lacks the nullable-identifier convention, serving as a concrete negative example. Adds a domain-neutral WidgetRequestDTO/WidgetDTO example alongside the PunishTemplate one.
…se state main moved to @Version(ApiVersion.V1)-based API versioning (#110) and removed the JWT/@secured auth system entirely (#109) since this skill was written - its code examples still showed the old @controller(value = ApiVersion.V1 + "/x") URI-prefix pattern and @secured({Roles...}) annotations, neither of which exist anymore. Also swaps the removed ConnectorAuthController for ConnectorController in the "known non-conforming" list.
…se state Same drift as backend-controller-layer: the JWT/@secured auth system is gone and API versioning moved from a URI prefix to @Version(ApiVersion.V1) - the example controller snippet still showed both the removed @secured annotation and the old @controller(value = ApiVersion.V1 + "/x") prefix pattern.
…state Drops the stale @secured reference (the JWT/@secured auth system was removed from the backend entirely) in favor of the actual controller- level annotations (@Version/@controller), and swaps the removed ConnectorAuthController for ConnectorController in the "known non-conforming" list.
The full PunishmentTemplateController sketch still imported the removed io.micronaut.security.annotation.Secured and net.onelitefeather.blackhole.backend.security.Roles, and used the old @controller(value = ApiVersion.V1 + "/template") URI-prefix pattern. Both were removed from the real backend by #109 (auth system removal) and #110 (@Version-based API versioning); the example now matches.
…ntroller-layer Rewrites the skill so the rule and its primary Widget example apply to any Micronaut backend that separates controller/service/repository layers, instead of asserting Blackhole-specific facts (hardcoded package path, "this codebase has no auth annotation", a fixed "known non-conforming controllers" list). Blackhole and Otis now appear as an "Observed in real Micronaut codebases" evidence section rather than the skill's baseline assumption.
…ce-layer Same generalization as micronaut-controller-layer: the rule and its Widget example now apply to any Micronaut backend, with Blackhole's feature-package services (elo/, appeal/, punishment/, imports/) and Otis's missing service layer moved to an "Observed in real Micronaut codebases" evidence section rather than the skill's baseline assumption.
…enapi-contract Same generalization as the other layer skills: the *Api-interface rule and its Widget example now apply to any Micronaut project using swagger-annotations, with Blackhole's PunishmentTemplateApi and Otis's inline annotation bulk moved to an "Observed in real Micronaut codebases" evidence section.
…ntract Same generalization as the other layer skills: the RequestDTO/DTO convention and its Widget example now apply to any Micronaut project, with Blackhole's PunishTemplateDTO pair and Otis's single-DTO OtisPlayerDTO anti-pattern moved to an "Observed in real Micronaut codebases" evidence section rather than the skill's baseline assumption.
Codifies "always respond with a defined DTO, never an exception directly": service methods that can fail return a sealed *ResponseDTO (success record + error record(s) implementing a shared ErrorResponse marker interface), and the controller pattern-matches purely to pick the HTTP status while always keeping the DTO as the body - a global exception handler stays a last-resort safety net that itself returns a defined ErrorResponse DTO. Derived from Vulpes-Backend's ItemModelResponseDTO/ItemServiceImpl and confirmed consistent across its Item/Font/Notification/Attribute/Sound domains.
…ther layer skills Points controller-layer, service-layer, dto-contract, and openapi-contract at the new error-response-contract skill wherever they touch error-path behavior, and flags the bodyless HttpResponse.notAllowed()/.notFound() calls in micronaut-controller-layer's own example as simplified rather than the recommended shape.
…aut-dto-contract Per feedback, a resource's request and response shapes now live under ONE sealed marker interface (WidgetDTO) nesting CreateRequest (no identifier field), UpdateRequest (required identifier), Response (success), and Error (implements the shared ErrorResponse interface) - rather than two separate DTOs plus a separate sealed *ResponseDTO. This makes the previous two-skill split (dto-contract vs error-response-contract) redundant, since both described facets of what's now a single type; the error-response-contract content is folded in here. Also newly requires resource-qualified @Schema(name) on every nested variant, since multiple resources' identically-named nested records (Response/Error/CreateRequest) would otherwise collide in the generated OpenAPI spec.
Content is now part of micronaut-dto-contract's unified sealed WidgetDTO shape (CreateRequest/UpdateRequest/Response/Error) - see that skill's history for the full error-handling rule this used to carry on its own.
…tDTO shape Updates the before/after example and the "no business branching" rule to match micronaut-dto-contract's new single-sealed-interface convention (WidgetDTO.CreateRequest/UpdateRequest/Response/Error) instead of the retired separate WidgetRequestDTO + CreateOutcome pairing, and drops the now-removed micronaut-error-response-contract cross-reference.
…O shape Updates the rule, checklist, and example to match micronaut-dto-contract's new single-sealed-interface convention - services take the specific CreateRequest/UpdateRequest variant they need and return WidgetDTO.Response/Error directly instead of a separate CreateOutcome/UpdateOutcome type, and create-vs-update is now resolved by which request type is passed rather than by branching. Drops the micronaut-error-response-contract cross-reference and notes that the Blackhole worked-example reference file predates this convention.
…tDTO shape Updates the WidgetApi example to document WidgetDTO.CreateRequest/ UpdateRequest as request bodies and WidgetDTO.Response/Error as response schemas per micronaut-dto-contract, replacing the retired 405-identifier-conflict response with a 404 that correctly references WidgetDTO.Error's schema instead of being left bodyless. Drops the micronaut-error-response-contract cross-reference.
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.
Summary
Adds four scoped Claude Code skills under
.claude/skills/that codify the layered architectureBlackhole's backend controllers should follow (controller -> service -> repository, with
OpenAPI docs and DTOs as separate cross-cutting concerns):
backend-controller-layer- controllers are thin HTTP adapters only: routing,@Secured, oneservice call per method, response mapping. No repository injection, no inline business logic,
no OpenAPI annotations.
backend-service-layer- business/persistence logic lives in a@Singletonservice colocatedin its feature package (mirroring the existing
elo/,appeal/,punishment/,imports/precedent), returning small outcome records/enums instead of bare entities/exceptions.
backend-openapi-contract-@Operation/@ApiResponse/@Schemaannotations move to adedicated
*Apiinterface the controller implements, instead of bloating the implementation.backend-dto-contract- the*RequestDTO(nullable identifier = create/update) + full*DTOpairing already used by
PunishTemplateRequestDTO/PunishTemplateDTO, documented as thecanonical shape.
Each skill was derived from analyzing
PunishmentTemplateHandler.java(repository injectedstraight into the controller, create/update/existence logic inline, ~30-40 lines of Swagger
annotations per endpoint) and includes a full worked refactor example. Each skill also
cross-references
OneLiteFeatherNET/Otis, a sibling Micronaut backend, which shows the exactsame smells (repository-in-controller, inline business checks, no
*Apiinterface, one DTOreused for both request and response) - confirming this is a recurring org-wide pattern rather
than a one-off, and each skill adds a domain-neutral
Widget*example alongside theBlackhole-specific one so the rule reads as general guidance.
No production source files were changed - this PR only adds skill/documentation files.
Test plan