Skip to main content

server-arch-gates

Apply Artemis architecture rules when changing server Java or diagnosing an ArchUnit failure.

来源信息

仓库
ls1intum/Artemis
最近来源活动
2026年9月25日 15:27
检测到的 SKILL.md 语言
英语
星标
813
分支
395

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
2 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
server-arch-gates
description
Apply Artemis architecture rules when changing server Java or diagnosing an ArchUnit failure.
# Server architecture gates Artemis enforces server conventions with ArchUnit tests under `src/test/java`. This skill maps server changes to those checks and explains rules whose failure messages lack context. ## Run them locally The whole architecture suite, which is what the Server Code Style job runs: ```bash ./gradlew test -DincludeTags='ArchitectureTest' -x webapp ``` This is much faster than the full server test suite. Run it before pushing any change to `src/main/java`. A violation fails both Server Code Style and Server Tests, so it is worth catching locally. A single class while iterating: ```bash ./gradlew test --tests ArchitectureTest -x webapp ``` ## Which rules apply to what you changed | You changed | Read | | -------------------------------------- | --------------------------------------------------- | | A service or REST resource | Transactions, persistence access, module boundaries | | A repository | Transactions, raw JDBC | | A DTO record | DTO conventions | | Anything holding state across requests | Caching, distributed data | | An entity or an association | Caching, entity conventions, column mapping | | Anything at all in a large file | Counted gates | | Anything that lowercases or uppercases | Case conversion | | Anything that serializes JSON | Jackson version | | A websocket topic or message handler | Websocket topics | The detail for each, with the reason and the failing rule name, is in `reference/gates.md`. Read it rather than guessing; several of these rules forbid something that looks completely reasonable. **Jackson 2 must not appear in production code.** Artemis serializes with Jackson 3, whose packages are `tools.jackson`. Jackson 2 stays on the runtime classpath for third-party libraries that carry their own mapper, so a `com.fasterxml.jackson.databind`, `.core`, `.dataformat`, `.datatype`, `.module`, `.jr` or `.jaxrs` import still compiles — `testNoJackson2InProductionCode` in `ArchitectureTest` is what rejects it. The one exception is `com.fasterxml.jackson.annotation`: `jackson-annotations` never moved to the `tools.jackson` group, so `@JsonInclude`, `@JsonProperty` and `@JsonTypeInfo` stay where they are and must not be "fixed". Mappers are immutable in Jackson 3 — derive one with `JsonMapper.builder()` or `rebuild()`, never `configure()` or `registerModule()` on a built instance — and its exceptions are unchecked, so a `catch (IOException)` no longer catches a parse failure. ## The rules most often broken **No transaction boundaries in services or controllers.** `@Transactional`, `TransactionTemplate`, and `PlatformTransactionManager` belong in repositories, typically on modifying queries, and `TransactionSynchronizationManager` is banned outright. Enforced globally by `testTransactionBoundariesOnlyInRepositories`, `testNoProgrammaticTransactionManagement` and `testNoTransactionSynchronization` in `src/test/java/de/tum/cit/aet/artemis/shared/architecture/ArchitectureTest.java`. The replacements are a check in the `WHERE` clause of a `@Modifying` query, or explicit compensation in a `catch` block. **No direct persistence access.** No injected `EntityManager` or `EntityManagerFactory`, and no `JdbcClient`, `JdbcTemplate`, or `DataSource`. Write the statement as a `@Query` on a repository, with `nativeQuery = true` where there is no entity to name. Enforced by `shouldNotUseEntityManagerDirectly` and `shouldNotUseRawJdbcDirectly` in `src/test/java/de/tum/cit/aet/artemis/shared/architecture/ArchitectureTest.java`. Three classes sit on that rule's exception list, carrying a TODO to refactor them away. One of them is `TitleCacheEvictionService`, which holds an `EntityManagerFactory` purely to reach the Hibernate `EventListenerRegistry` and register itself as a listener. So when the caching section below calls it the canonical eviction pattern, copy its eviction logic, not its constructor: a new class doing the same thing fails the rule, because the list is grandfathering rather than permission. Raw JDBC has no per-class exceptions at all; only `core.config` may hold a `DataSource`. **Never touch Hazelcast or Redis directly.** All cross-node state goes through `DistributedDataProvider` in `src/main/java/de/tum/cit/aet/artemis/core/service/distributed/`. Enforced by `src/test/java/de/tum/cit/aet/artemis/shared/architecture/DistributedDataProviderArchitectureTest.java`. The provider is configurable, so direct usage does not fail loudly, it silently loses the state. **No `@Lob`.** A CLOB on PostgreSQL is a large object, so the value lands in `pg_largeobject` and the column keeps only its id - while the long text columns here are Liquibase `longtext` or `clob`, both `text` on PostgreSQL, holding the text itself. A `String` or a converted attribute needs no annotation at all; for a structured value use `@JdbcTypeCode(SqlTypes.JSON)` over a `json` column. Enforced by `testNoLobAnnotation` in `ArchitectureTest.java`. **No Hibernate second-level cache.** No `@Cache` on entities or associations. Enforced by `testNoHibernateSecondLevelCacheAnnotation` in `ArchitectureTest.java`. For DTO and projection caching use Spring `@Cacheable`, always paired with explicit eviction. **Reach optional modules through their API.** Use `Optional<*Api>`, never another module's repository directly. **Never fold case without a locale.** `String.toLowerCase()` and `String.toUpperCase()` use the JVM default locale, so the same input gives a different answer depending on where the server runs. Pass `Locale.ROOT` for machine-facing values and `Locale.ENGLISH` only where the surrounding code already does for that kind of value. Enforced by `testNoLocaleLessCaseConversion` in `ArchitectureTest.java`, over production and test classes both. **Every websocket destination is a declared topic.** Declare a `WebsocketTopic` with its `WebsocketTopicAccess` rule, or a `WebsocketUserTopic` for data of one user, as a `public static final` constant of the module's `web/<Module>WebsocketTopics` class, and send with `websocketMessagingService.sendMessage(TOPIC.at(id), dto)`. A subscription to an undeclared destination is rejected, so a topic that is sent but not declared silently reaches nobody. Clients send only to `/app/...` destinations handled by `@MessageMapping` methods, which check the sender themselves. Enforced by `WebsocketTopicArchitectureTest` in `src/test/java/de/tum/cit/aet/artemis/shared/architecture/`. ## Before adding a cache The default answer is not to. The bar is a measured performance gain that justifies the eviction-correctness work, because there is no service-level transaction boundary to coordinate eviction within a request. See `documentation/docs/developer/guidelines/caching.mdx` for the full rationale, and `reference/gates.md` for the pattern if you do proceed. ## Adding a capability to the distributed data layer If `DistributedDataProvider` lacks what you need, add it there, implement it for all three providers (Hazelcast, Redis, Local), and add a case to `AbstractDistributedDataTest`. That suite is what keeps the providers in agreement. Request entry lifetimes at the call site with `getExpiringMap(name, ttl)`; `getMap(name)` rejects a per-entry TTL deliberately, because a provider map configuration only applies to that one provider. Full guidance: `documentation/docs/developer/guidelines/distributed-data.mdx`.
在 GitHub 查看