One annotation, @Concern(KIND), records why a class or package exists when that reason is a
technical concern rather than the BC's responsibility. An import search says what a class
touches; the marker says what it is for. One search over the marker returns a complete,
intent-based surface — including members no import search finds (an in-process engine binding
is an external system without a single network import).
The Annotation
- one
Concern.javain the project root package — an application-wide vocabulary no BC owns @Documented,@Retention(SOURCE),@Target({TYPE, PACKAGE}), single elementKind value(), nestedenum Kind- SOURCE retention — every consumer (reader, agent, javadoc) reads source; the jar stays free of the metadata
- full template with both starter kinds: references/concern-template.md
Placement
- package-level (on
package-info.java) when the whole BC serves the concern — state it once, not on every class - type-level when members are scattered across BCs that serve something else
- never the same kind on both a package and a type inside it — one fact, one place
- a type may carry a kind its package does not declare
The Sole-Purpose Rule
- mark only what would not exist without the concern; litmus: "this class would not exist without ___"
- participation is not membership — a domain class emitting an event in passing, or an orchestrator triggering provider calls, is not marked
- each kind's doc comment states what does not qualify; the exclusions are where the marker earns its keep
Kind Admission
- keep the enum a small closed set (about five kinds); every added constant dilutes the others
- a kind that fits nearly every class carries no information — no
LOGGING - a concern is implemented by the application, not provided by the platform (Java SE, MicroProfile, Jakarta EE) — building on a platform API (JFR, OTEL) still counts as application-implemented; a feature the platform provides outright (container transactions, health machinery) does not
- corollary: do not duplicate a canonical stack marker —
@Transactional,@Liveness,@Retryalready say it; a second vocabulary for the same fact drifts - grep-findability does not disqualify a kind — naming the intent is worth it even when an import search finds the set
- heuristic: a kind usually names the essential implementation of a non-functional requirement — caching for performance, observability for diagnosability — significant, but not domain-related, and at least partially scattered across BCs; trace it to the requirement it implements; a candidate that is neither is a business component or noise, not a kind
- a kind whose membership must be argued case by case is a tagging system, not a classification — sharpen the definition or drop the kind
Kind Naming
- name the judgment, not the mechanism —
EXTERNAL_SYSTEM, notEGRESSorOUTBOUND; direction and transport words misclassify edge members (the in-process engine, the loopback client) - the name must classify every member without commentary; a name that needs a footnote is worse than a longer one
- no dangling adjectives — the name completes "exists to/for ___"
Starter Kinds
OBSERVABILITY— exists to record what the process did, whatever the transport: JFR events, OTEL spans and metrics, dedicated diagnostic loggers; the emitting API is not the criterion; a class that emits or logs in passing is not thisEXTERNAL_SYSTEM— exists to communicate with a system that has its own lifecycle and failure modes: another process, a remote service, or a foreign in-process engine; ownership is not the criterion — the project's own server counts; owning the shared HTTP client or merely triggering the call is not this- candidates that pass admission when the project has them:
MIGRATION(exists only to bridge to the legacy system — the deletion list),SECURITY(authn/authz decision points),CACHING(cache stores and invalidation policies — not the classes reading through them; where the stack has a canonical marker like@CacheResult, that vocabulary wins)
Boundaries
/bceand/java-conventionsnaming rules hold unchanged: packages and classes stay named after responsibilities — the annotation carries the technical-concern identity so names never have to- the marked set is maintained by review — keep each kind small enough to audit in one reading; no generated enforcement test
- docs and markers only; introducing the vocabulary changes no behavior
Composition
/java-conventionsowns language-level style;/bceowns where BCs, packages, andpackage-info.javalive- this skill owns only the concern vocabulary and its rules; the composed skill always specializes, never contradicts