diff --git a/STACK.md b/STACK.md index 1f2874c..ccfbe40 100644 --- a/STACK.md +++ b/STACK.md @@ -12,27 +12,27 @@ |-------|-----------|-----| | **Language** | Scala 3.3+ LTS | VirtusLab maintains the compiler. Scala 3 has cleaner syntax, given/using for DI, extension methods, enums, union types | | **Runtime** | JDK 21+ with Project Loom | Virtual threads = millions of lightweight threads, no async/callback hell | -| **HTTP Server** | Tapir + JDK HttpServer | Tapir gives type-safe endpoint definitions with auto-generated OpenAPI docs. JDK HttpServer with `Executors.newVirtualThreadPerTaskExecutor()` is the lightest possible server | +| **HTTP Server** | Tapir + Netty | Tapir gives type-safe endpoint definitions with auto-generated OpenAPI docs. Netty-based server for production use | | **Concurrency** | Ox | Structured concurrency, Go-like channels, error supervision, retries, rate limiting. Built on virtual threads | | **Database** | Magnum | Scala 3 native, typesafe SQL interpolator, auto-generated CRUD, no dependencies, works with any JDBC database | -| **Migrations** | Flyway | Industry standard, JVM-native, works with any JDBC database | +| **Migrations** | Liquibase | Supports rollbacks in free version (Flyway does not). Rollbacks are essential for dev workflow — drop last migration and fix, like in WinterCMS | ## Supporting Libraries | Concern | Library | Notes | |---------|---------|-------| -| **JSON** | jsoniter-scala | Fastest JSON lib on JVM — author hand-tunes CPU instructions emitted by JVM. Only non-broken Scala JSON lib. May need a custom wrapper (~75% done) | -| **Config** | [Jig](https://github.com/lbialy/jig) (HOCON) | Supports reading & writing including comments. Better discoverability for LLMs. Maintained by VL engineer with full control — we can add features as needed | -| **Logging** | Scribe + Scribe SLF4J binding | Excellent performance, configured in code (no XML). SLF4J binding for Java lib compat. Avoid direct SLF4J — its config resolution rules are a nightmare | +| **JSON** | jsoniter-scala | Fastest JSON lib on JVM. May need a custom wrapper (~75% done) | +| **Config** | [Jig](https://github.com/lbialy/jig) (HOCON) | Supports reading & writing including comments. Better discoverability for LLMs. Maintained in-house — we can add features as needed | +| **Logging** | Scribe + Scribe SLF4J binding | Excellent performance, configured in code (not XML). SLF4J binding for Java library compatibility | | **Caching** | Caffeine (in-memory) + Jedis/Lettuce (Redis) | Caffeine is the fastest JVM cache; Redis for distributed | -| **Hashing** | jBCrypt + JDK crypto | Password hashing + general encryption. JDK is good enough; Bouncy Castle via JNI if performance-critical | -| **Email** | Custom `summer-postcard` with driver trait | Jakarta Mail is legacy with Java failure modes (random NPEs). Emails are too critical for that. Need clean interface + multiple bindings: SMTP, AWS SES, etc. | +| **Hashing** | jBCrypt + JDK crypto | Password hashing + general encryption. Bouncy Castle via JNI if performance-critical | +| **Email** | `summer-postcard` with driver trait | Clean interface + multiple drivers: SMTP, AWS SES, etc. | | **HTTP Client** | sttp | Best HTTP client on JVM. Pairs with Tapir (same ecosystem). Handles sessions, cookies, streaming, HTTP auth | | **Admin Frontend** | Vue.js 3 + TypeScript | SPA admin panel consuming Tapir API | -| **OpenAPI codegen** | Tapir -> OpenAPI -> openapi-typescript | Auto-generated TS types from Tapir endpoint definitions. VL has working template for this. Enables 1:1 API bindings + drift detection | -| **Template Engine** | Pebble | Twig-like syntax (preserves WinterCMS familiarity). Scalate is abandonware — avoid. Needs investigation: file-based loading + hot-reload in dev (file watcher fallback if needed) | -| **CLI** | case-app | By Alex Archambault (original scala-cli author). Auto-generated help, parsed case classes. Scopt has bad assumptions; Decline is too complex for LLMs to use effectively | -| **Testing** | MUnit + testcontainers-scala + Tapir test utils | MUnit only — no ScalaTest (build issues, unreliable cross-platform publishing). testcontainers-scala for integration tests (Postgres etc.) | +| **OpenAPI codegen** | Tapir -> OpenAPI -> openapi-typescript | Auto-generated TS types from Tapir endpoint definitions. Enables 1:1 API bindings + drift detection | +| **Template Engine** | Pebble | Twig-like syntax (preserves WinterCMS familiarity). Needs investigation: file-based loading + hot-reload in dev | +| **CLI** | case-app | Auto-generated help, parsed case classes, simple and LLM-friendly | +| **Testing** | MUnit + testcontainers-scala + Tapir test utils | MUnit for unit tests, testcontainers-scala for integration tests (Postgres etc.) | | **Build (project)** | sbt | Multi-module build for the subprojects | | **Build (scripts)** | scala-cli | Quick scripts, prototyping, dev tooling. VirtusLab maintains it | @@ -51,15 +51,16 @@ Each module = separate sbt subproject, publishable independently (like Illuminat | **Events** | `summer-festival` | Simple event bus with typed events + Ox channels for async | | **Http** | `summer-surf` | Tapir endpoint definitions + sttp client, request/response wrappers, routing (Tapir endpoints are type-safe routes) | | **Database** | `summer-lagoon` | Magnum repos + immutable case class models + query builder + pagination. **Key challenge:** Scala immutability vs Eloquent/Doctrine mutation patterns — use `copy()` + repo.save() for single entities. Relations are the hardest part to model (see open questions) | -| **Auth** | `summer-bouncer` | JWT tokens (for API) + session cookies (for admin), guards as traits, session management. JWT needs separate investigation — no good Scala-native JWT lib yet, may use Spring's JWT implementation | +| **Auth** | `summer-bouncer` | JWT tokens (for API) + session cookies (for admin), guards as traits, session management. JWT via proven Java JWT library | | **Validation** | `summer-lifeguard` | Compile-time via Scala types + runtime rules engine (inspired by fields.yaml) | | **Cache** | `summer-cooler` | Caffeine + Redis, driver-based via trait | -| **Queue** | `summer-conga` | Ox channels + virtual threads for workers, persistent queue via DB or Redis | -| **Mail** | `summer-postcard` | Clean email interface with driver trait + template support. Drivers: SMTP, AWS SES, etc. No Jakarta Mail dependency | +| **Queue** | `summer-conga` | Persistent queue for offloading heavy work from the HTTP hot path. Driver-based: Postgres table, ActiveMQ, Redis, etc. Guarantees that enqueued jobs are not lost and are eventually executed. Virtual threads for workers | +| **Mail** | `summer-postcard` | Clean email interface with driver trait + template support. Drivers: SMTP, AWS SES, etc. | | **Console** | `summer-bonfire` | CLI command framework via case-app for scaffolding (`summer create:plugin`, etc.) | | **Filesystem** | `summer-sandcastle` | `java.nio.file` + pluggable storage providers (S3, etc.) via driver trait | | **Translation** | `summer-phrasebook` | i18n with HOCON/JSON locale files | | **View** | `summer-sunset` | Template engine for admin panel rendering | +| **Plugins** | `summer-party` | Plugin system: versioned plugin interfaces, resolution system, ServiceLoader-based runtime discovery. Private plugin registry with version compatibility extracted from published artifacts | ### Illuminate Packages Not Ported (and why) @@ -68,20 +69,22 @@ Each module = separate sbt subproject, publishable independently (like Illuminat | **Pipeline** | Function composition is native to Scala (`f andThen g`). Laravel needs a Pipeline class because PHP lacks first-class functions. In Scala this is a one-liner, not a module. | Inline wherever needed | | **Routing** | Tapir endpoint definitions ARE routes — defining an endpoint and defining a route is the same thing. No separate routing layer needed. | Merged into `summer-surf` (http) | | **Session** | For an API-first CMS with JWT, sessions are thin — only needed for admin panel cookies. Not enough to justify a standalone module. | Merged into `summer-bouncer` (auth) | -| **Log** | Scribe handles logging directly with code-based configuration. No XML config hell, no SLF4J resolution rules. SLF4J binding provided for Java library compatibility. No need for a separate module. | Direct Scribe usage | +| **Log** | Scribe handles logging directly with code-based configuration. SLF4J binding provided for Java library compatibility. | Direct Scribe usage | | **Collections** | Scala stdlib collections are already excellent — `List`, `Map`, `Seq`, `Vector` with `map`, `filter`, `fold`, etc. No wrapper needed. | Scala stdlib | -| **Pagination** | A paginator is a case class + a few helper methods. Too small for a standalone module. | Merged into `summer-lagoon` (database) | +| **Pagination** | Two aspects: DB-level query pagination (in `summer-lagoon`) and template/API pagination helpers (in `summer-surf` / `summer-sunset`). Not enough for a standalone module. | Split across `summer-lagoon` + `summer-surf` | --- ## Plugin & Theme System -### Plugins +### Plugins (`summer-party`) -Each plugin = a directory with a `Plugin.scala` descriptor (same lifecycle as WinterCMS). +Plugins are published JVM artifacts (JARs), discovered at runtime via **ServiceLoader**. -- Descriptor declares: models, controllers, components, console commands, event listeners, navigation items -- Plugins discovered at boot via classpath scanning or manifest +- `summer-party` defines versioned plugin interfaces — a common contract all plugins implement +- Plugins discovered at boot via ServiceLoader (runtime-based, not classpath scanning) +- Version compatibility: registry extracts interface versions from published artifacts to know which plugins work with which CMS version +- Plugin descriptor declares: models, controllers, components, console commands, event listeners, navigation items - Plugins can extend other plugins' models via Scala 3 extension methods + event hooks - Plugin lifecycle: `register()` -> `boot()` @@ -159,6 +162,7 @@ summercms/ summer-sandcastle/ # Filesystem + storage providers summer-phrasebook/ # Translation / i18n summer-sunset/ # View / templates + summer-party/ # Plugin system + registry app/ # Full CMS application (depends on all modules) summer-cms/ # Assembled CMS with admin panel plugins/ # Example/core plugins @@ -193,6 +197,7 @@ Single-entity CRUD via `copy()` + repo is straightforward. **Relations are the h ### Other open items - **Pebble hot-reload**: Can Pebble load templates from filesystem (not classpath) and hot-reload on change? If not, implement file watcher that reloads the template engine on file changes. See [Pebble docs](https://deepwiki.com/PebbleTemplates/pebble) -- **JWT library**: No good Scala-native JWT solution. Evaluate Spring's JWT implementation or write a thin wrapper. sttp/Tapir handle HTTP auth but not JWT token generation/validation +- **JWT library**: Evaluate proven Java JWT libraries for token generation/validation - **jsoniter-scala wrapper**: ~75% complete wrapper exists. Evaluate if it needs finishing or if raw jsoniter-scala is sufficient -- **API drift detection**: VL has know-how for static verification that services connect correctly post-deployment (from Yaga/Besom project). Integrate into dev loop and CI +- **API drift detection**: Static verification that services connect correctly post-deployment. Integrate into dev loop and CI +- **Plugin registry**: Design private plugin registry — extract interface versions from published artifacts to determine CMS version compatibility