Skip to main content

server-arch-gates

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

소스 정보

저장소
ls1intum/Artemis
최근 소스 활동
2026년 10월 6일 23:05
감지된 SKILL.md 언어
영어
스타
816
포크
396

설치 방법

기본적으로 소스를 먼저 확인하는 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. ## Configuration lifecycle Course and exercise settings use permanent default rows. Create courses with `CourseRepository.saveWithDefaultConfigurations`; this initializes settings even when an optional module is disabled. Every path that stores a new exercise (creation, every kind of import, copies) calls `ExerciseConfigurationService.initialize` right after the first save; it inserts the team and plagiarism rows with a plain INSERT after an unlocked existence read, and applies the request's settings in place, and nothing creates a missing row later. Programming creation/import also persists build settings. Use modifying queries for feature switches and settings endpoints, preserving configuration ids. Do not recreate or remove settings when disabling a feature. Backfill existing owners with the Liquibase migration skill. See [database](../../documentation/docs/developer/guidelines/database.mdx#permanent-configuration-rows). ## 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에서 보기