Imported from mingchiuli/megalith-micro (
AGENTS.md). Install upstream withnpx skills add mingchiuli/megalith-micro. Copyright stays with the author.
AGENTS.md - Megalith Micro
Operating notes for AI coding agents working in this repository. Read README.md for the complete architecture, application responsibilities, deployment model, and frontend SSR flow; this file records the implementation constraints that are easy to miss.
Toolchain
- Java 25 (GraalVM HotSpot), Spring Boot 4.1.1, Hibernate ORM 7.4.6.Final, Redisson 4.7.0, and Caffeine.
- Gradle 9.7 Kotlin DSL; the root
build.gradle.ktsconfigures all Java subprojects. - Rust 2024 for
micro-gateway-rsandmicro-sync-rs. - Bun 1.4.2, Vue 3, and Vite for the standalone
micro-frontendservice.
JAVA_HOME must point to a GraalVM HotSpot JDK, not the Espresso JVM. On macOS, for example:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-25.3.4.1+1.1/Contents/Home
Checks and Commands
./gradlew build
./gradlew :micro-auth:test
./gradlew :micro-auth:nativeCompile
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
# Requires Redis 8 on 127.0.0.1:6379.
MICRO_SYNC_TEST_REDIS_URL=redis://127.0.0.1:6379/ \
cargo test -p micro-sync-rs redis_store_round_trip_when_configured -- --ignored
bun install --frozen-lockfile
bun run frontend:check
bun run frontend:build
Java tests include ArchUnit and Spring AOT processing; keep reflection and runtime hints in sync. Java production artifacts are GraalVM Native Images, Rust services are release binaries, and the frontend is a Bun standalone executable. Do not add deployment assumptions that require a JVM, JRE, Rust toolchain, Bun runtime, or source tree in a production image.
Repository Shape
| Area | Modules | Responsibility |
|---|---|---|
| Applications | micro-auth, micro-user, micro-blog, micro-exhibit, micro-search |
Java native services |
| Rust applications | micro-gateway-rs, micro-sync-rs |
Gateway proxy and Redis-backed collaboration |
| Frontend | micro-frontend |
Vue SSR, hydration, and embedded static assets |
| Contracts | api-auth, api-user, api-blog, api-search |
Typed HTTP interfaces and RPC models |
| Shared Java | common-* |
Contract, RPC, web, auth, observability, messaging, scheduling, outbox, export |
| Cache | cache |
Caffeine L1, Redis L2, and distributed eviction |
The frontend is an independent Bun workspace outside Gradle and Cargo. Dependency versions belong
in the root package.json catalog and bun.lock; workspace packages use catalog: references.
Architecture Invariants
- Single-pass authorization.
micro-gateway-rscallsPOST /inner/auth/routeonce. The auth service returns the target host/port and a Base64URL principal inX-Megalith-Principal; business services trust that header and never re-derive identity from cookies. - SSR and browser traffic are separate. SSR prefetch uses
SSR_API_BASE_URLthrough the gateway. Browser API and WebSocket traffic goes through nginx and the gateway. Tokens stay in HttpOnly cookies; browser code must not read or persist access or refresh tokens. SSR requests must create isolated Router, Pinia, i18n, head-management, and HTTP state. - Stateless collaboration.
micro-sync-rsreplicas coordinate through shared Redis Streams and state for documents, awareness, snapshots, presence, leases, and compaction. Do not add sticky session or room ownership assumptions. @Cacheowns cache keys. Keys are<namespace>:v<version>:<sha256(canonicalTypedArgs)>fromCacheKeyFactory. Cache methods must go through the Spring proxy. Reads are Caffeine L1 then Redis L2; misses execute the method and write both levels with the annotation TTL. Increase the explicit version for incompatible contracts.- Eviction is exact and broadcast.
AuthCacheKeys/CacheEvictoruse sharedCacheDescriptorconstants, acquire the same distributed key locks as reads, delete exact Redis keys, invalidate local L1, and broadcast through confirmed RabbitMQ fanout or a Redis reliable topic. There are no reflective method lookups in eviction. - Ports and adapters. Core Java code uses
domain,application.model,application.port.in,application.port.out,application.service,adapter.in.*,adapter.out.*, andconfig. Input adapters call input ports; application services depend on output ports, never concrete HTTP, persistence, Redis, Elasticsearch, or storage adapters.*HttpServiceWrapperclasses unwrapRemoteResult.requireSuccess(...)behind directory/gateway ports. Spring Data repositories belong underadapter.out.persistence.repository. - Transaction boundary. Services prepare inputs across reads without a transaction.
Transactional persistence adapters (including existing
*Wrapperclasses) do only writes and the matching outbox insert in one short transaction and never query back. Do not add@Transactionalto services or repository reads to transactional writers. - Transactional outbox. User and blog changes commit to
m_outbox_eventbefore confirmed RabbitMQ publication. Cache eviction and Elasticsearch indexing consume those events. User deletion usesUserDeletedMessageon its dedicated fanout exchange;micro-blogconsumes it to delete blogs owned by the deleted users. Never publish domain events outside the outbox. - JPMS.
cacheexports only its publicannotation,handler, andkeypackages. A new public package needs anexportsentry; downstream JPMS modules requirewiki.chiu.micro.cache. - Native and AOT reachability. Types used through reflection, serialization, HTTP interfaces, or native-image initialization need the matching Spring AOT/runtime hints.
- Observability. Java, Rust, Bun, gateway, and sync services export correlated OpenTelemetry traces, metrics, and logs; preserve existing trace-context propagation when adding boundaries.
- External Lua sources. Redis scripts live in standalone
.luafiles. Java reads them as classpath resources and registers the required Native Image resource hints; Rust loads them withinclude_str!. Never embed Lua bodies in Java or Rust string literals, and do not require a source tree beside a production executable. - CI and rewritten history. A force-push event may provide a
github.event.beforeSHA that is no longer reachable.fetch-depth: 0does not fetch deleted objects. Workflows diffing event SHAs must verifygit cat-file -e "$BEFORE^{commit}"and use a conservative full-build path when it is missing. Do not rewrite shared history without explicit approval, a completegit bundlebackup, and--force-with-lease; old commit URLs, PR refs, caches, and local stashes may retain old objects. - Rust boundaries match service responsibilities.
micro-sync-rskeeps protocol and state indomain, orchestration and store traits inapplication, Axum delivery inadapter.inbound, and Redis connections, errors, keys, Stream IDs, workers, and Lua calls inadapter.outbound.redis. Inbound adapters callRoomManagerrather than the concrete store.micro-gateway-rsremains a transport-oriented edge service: authentication belongs toclient, no-I/O forwarding rules belong toproxy, and handlers/middleware coordinate them without an artificial domain layer. - Common module packages. Every shared module uses
wiki.chiu.micro.common.<module>as its root package, socommon-messagingownswiki.chiu.micro.common.messaging,common-schedulingownswiki.chiu.micro.common.scheduling, andcommon-outboxownswiki.chiu.micro.common.outbox.common-outboxmirrors the application layout withdomain,application,adapter.in.actuator,adapter.out.persistence.repository, andconfig.common-contractgroups contracts by kind underresult,error,message,model,enums, andconstant; do not recreate the retiredwiki.chiu.micro.common.langbucket.
Code Style
- Java uses 4-space indentation, sorted imports, no unused imports, and the surrounding method and
comment order. Preserve Chinese Javadoc conventions in
micro-user. - A Java source file's physical directory must match its declared package in both
src/main/javaandsrc/test/java; Gradle may compile mismatches, but JDTLS reports them as errors. - RPC models are records in
api-*; preserve established hand-written builders such asAuthorityRpcVo.builder(). - Nullability uses
org.jspecify@NonNull. Jackson is Jackson 3'stools.jackson.databind.JsonMapper, notcom.fasterxml.jackson. - Server WebMVC is functional: use
*HandlerwithRouterFunctions, keep internal routes under/inner/..., and declare HTTP contracts inapi-*with@GetExchange/@PostExchange. Keep client paths such as/role/authorizationsaligned with server routes such as/inner/role/authorizations. - Rust changes follow Rust 2024 conventions and must pass
rustfmt; preserve existing async, error, and tracing patterns. - Frontend TypeScript and Vue changes follow existing patterns, Prettier, and ESLint. Run
bun run frontend:checkbefore committing frontend changes. - Keep comments short and explain non-obvious decisions only. Avoid unrelated refactors.
Commits
- Use Conventional Commits:
feat,fix,refactor,test,chore, orbuild. - Write an imperative, concise subject followed by a short body when needed.
- Do not add AI attribution or
Co-Authored-Bytrailers. Claude Code attribution stays disabled in the ignored.claude/settings.local.jsonwith emptyattribution.commitandattribution.pr. - Do not push or rewrite shared history unless explicitly requested.