Skip to content

docs(claude): add layered backend-architecture Claude Code skills#111

Merged
TheMeinerLP merged 24 commits into
mainfrom
docs/backend-layering-skills
Jul 8, 2026
Merged

docs(claude): add layered backend-architecture Claude Code skills#111
TheMeinerLP merged 24 commits into
mainfrom
docs/backend-layering-skills

Conversation

@TheMeinerLP

Copy link
Copy Markdown
Contributor

Summary

Adds four scoped Claude Code skills under .claude/skills/ that codify the layered architecture
Blackhole'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, one
    service call per method, response mapping. No repository injection, no inline business logic,
    no OpenAPI annotations.
  • backend-service-layer - business/persistence logic lives in a @Singleton service colocated
    in 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/@Schema annotations move to a
    dedicated *Api interface the controller implements, instead of bloating the implementation.
  • backend-dto-contract - the *RequestDTO (nullable identifier = create/update) + full *DTO
    pairing already used by PunishTemplateRequestDTO/PunishTemplateDTO, documented as the
    canonical shape.

Each skill was derived from analyzing PunishmentTemplateHandler.java (repository injected
straight 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 exact
same smells (repository-in-controller, inline business checks, no *Api interface, one DTO
reused 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 the
Blackhole-specific one so the rule reads as general guidance.

No production source files were changed - this PR only adds skill/documentation files.

Test plan

  • N/A - documentation-only change (Claude Code skill files), no build/test impact.

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.
@TheMeinerLP
TheMeinerLP merged commit 5d708ca into main Jul 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant