STACK.md update, based on initial lucas feedback
This commit is contained in:
65
STACK.md
65
STACK.md
@@ -21,17 +21,18 @@
|
|||||||
|
|
||||||
| Concern | Library | Notes |
|
| Concern | Library | Notes |
|
||||||
|---------|---------|-------|
|
|---------|---------|-------|
|
||||||
| **JSON** | uPickle or Circe | Serialization/deserialization for API + config |
|
| **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** | Typesafe Config (HOCON) | Industry standard JVM config, supports YAML-like nesting, env var substitution |
|
| **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** | SLF4J + Logback | Standard JVM logging — used directly, no wrapper 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 |
|
||||||
| **Caching** | Caffeine (in-memory) + Jedis/Lettuce (Redis) | Caffeine is the fastest JVM cache; Redis for distributed |
|
| **Caching** | Caffeine (in-memory) + Jedis/Lettuce (Redis) | Caffeine is the fastest JVM cache; Redis for distributed |
|
||||||
| **Hashing** | jBCrypt + JDK crypto | Password hashing + general encryption |
|
| **Hashing** | jBCrypt + JDK crypto | Password hashing + general encryption. JDK is good enough; Bouncy Castle via JNI if performance-critical |
|
||||||
| **Email** | Jakarta Mail | Standard JVM email |
|
| **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. |
|
||||||
|
| **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 |
|
| **Admin Frontend** | Vue.js 3 + TypeScript | SPA admin panel consuming Tapir API |
|
||||||
| **OpenAPI codegen** | Tapir -> OpenAPI -> openapi-typescript | Auto-generated TS types for Vue frontend |
|
| **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** | Scalate (Mustache/SSP) or Twirl | Server-rendered public-facing themes |
|
| **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** | Decline or Scopt | Console command parsing for scaffolding |
|
| **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 or ScalaTest + Tapir test utils | Unit + integration testing |
|
| **Testing** | MUnit + testcontainers-scala + Tapir test utils | MUnit only — no ScalaTest (build issues, unreliable cross-platform publishing). testcontainers-scala for integration tests (Postgres etc.) |
|
||||||
| **Build (project)** | sbt | Multi-module build for the subprojects |
|
| **Build (project)** | sbt | Multi-module build for the subprojects |
|
||||||
| **Build (scripts)** | scala-cli | Quick scripts, prototyping, dev tooling. VirtusLab maintains it |
|
| **Build (scripts)** | scala-cli | Quick scripts, prototyping, dev tooling. VirtusLab maintains it |
|
||||||
|
|
||||||
@@ -46,16 +47,16 @@ Each module = separate sbt subproject, publishable independently (like Illuminat
|
|||||||
| **Container** | `summer-backpack` | Scala 3 `given`/`using` for compile-time DI + lightweight runtime registry for plugins |
|
| **Container** | `summer-backpack` | Scala 3 `given`/`using` for compile-time DI + lightweight runtime registry for plugins |
|
||||||
| **Contracts** | `summer-pact` | Scala traits — all public APIs defined here |
|
| **Contracts** | `summer-pact` | Scala traits — all public APIs defined here |
|
||||||
| **Support** | `summer-towel` | Extension methods, utility types, base classes |
|
| **Support** | `summer-towel` | Extension methods, utility types, base classes |
|
||||||
| **Config** | `summer-compass` | Typesafe Config wrapper, HOCON files, standardized access across all modules |
|
| **Config** | `summer-compass` | Jig-based HOCON config, supports reading & writing with comments, standardized access across all modules |
|
||||||
| **Events** | `summer-festival` | Simple event bus with typed events + Ox channels for async |
|
| **Events** | `summer-festival` | Simple event bus with typed events + Ox channels for async |
|
||||||
| **Http** | `summer-surf` | Tapir endpoint definitions, request/response wrappers, routing (Tapir endpoints are type-safe routes) |
|
| **Http** | `summer-surf` | Tapir endpoint definitions + sttp client, request/response wrappers, routing (Tapir endpoints are type-safe routes) |
|
||||||
| **Database** | `summer-lagoon` | Magnum repos + case class models + query builder + pagination |
|
| **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 |
|
| **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 |
|
||||||
| **Validation** | `summer-lifeguard` | Compile-time via Scala types + runtime rules engine (inspired by fields.yaml) |
|
| **Validation** | `summer-lifeguard` | Compile-time via Scala types + runtime rules engine (inspired by fields.yaml) |
|
||||||
| **Cache** | `summer-cooler` | Caffeine + Redis, driver-based via trait |
|
| **Cache** | `summer-cooler` | Caffeine + Redis, driver-based via trait |
|
||||||
| **Queue** | `summer-conga` | Ox channels + virtual threads for workers, persistent queue via DB or Redis |
|
| **Queue** | `summer-conga` | Ox channels + virtual threads for workers, persistent queue via DB or Redis |
|
||||||
| **Mail** | `summer-postcard` | Jakarta Mail wrapper with template support |
|
| **Mail** | `summer-postcard` | Clean email interface with driver trait + template support. Drivers: SMTP, AWS SES, etc. No Jakarta Mail dependency |
|
||||||
| **Console** | `summer-bonfire` | CLI command framework for scaffolding (`summer create:plugin`, 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 |
|
| **Filesystem** | `summer-sandcastle` | `java.nio.file` + pluggable storage providers (S3, etc.) via driver trait |
|
||||||
| **Translation** | `summer-phrasebook` | i18n with HOCON/JSON locale files |
|
| **Translation** | `summer-phrasebook` | i18n with HOCON/JSON locale files |
|
||||||
| **View** | `summer-sunset` | Template engine for admin panel rendering |
|
| **View** | `summer-sunset` | Template engine for admin panel rendering |
|
||||||
@@ -67,7 +68,7 @@ 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 |
|
| **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) |
|
| **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) |
|
| **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** | SLF4J is already a logging facade. Wrapping a facade with another facade adds zero value. Just use SLF4J + Logback directly. | Direct SLF4J usage |
|
| **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 |
|
||||||
| **Collections** | Scala stdlib collections are already excellent — `List`, `Map`, `Seq`, `Vector` with `map`, `filter`, `fold`, etc. No wrapper needed. | Scala stdlib |
|
| **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** | A paginator is a case class + a few helper methods. Too small for a standalone module. | Merged into `summer-lagoon` (database) |
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ Each plugin = a directory with a `Plugin.scala` descriptor (same lifecycle as Wi
|
|||||||
### Themes
|
### Themes
|
||||||
|
|
||||||
- Theme = directory of templates + assets + config
|
- Theme = directory of templates + assets + config
|
||||||
- Templates rendered server-side (Scalate/Twirl) or served as SPA shell
|
- Templates rendered server-side (Pebble) or served as SPA shell
|
||||||
- Components (from plugins) can be placed in theme templates
|
- Components (from plugins) can be placed in theme templates
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -165,3 +166,33 @@ summercms/
|
|||||||
summer-plugin-blog/
|
summer-plugin-blog/
|
||||||
summer-plugin-pages/
|
summer-plugin-pages/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions & Research Needed
|
||||||
|
|
||||||
|
### summer-lagoon ORM design (showstopper)
|
||||||
|
|
||||||
|
In PHP/Eloquent, models are mutable and carry their own persistence:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$user = User::findByName("name");
|
||||||
|
$user->firstName = "Raziel";
|
||||||
|
$user->save();
|
||||||
|
```
|
||||||
|
|
||||||
|
In Scala, case classes are immutable. The equivalent pattern:
|
||||||
|
|
||||||
|
```scala
|
||||||
|
val user = UserRepo.findByName("name")
|
||||||
|
UserRepo.save(user.copy(firstName = "Raziel"))
|
||||||
|
```
|
||||||
|
|
||||||
|
Single-entity CRUD via `copy()` + repo is straightforward. **Relations are the hard part** — in Eloquent, relations are dynamic mutable fields on the model. Modeling this idiomatically in Scala with immutability needs deep design work. This is the biggest architectural risk in the project.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- **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
|
||||||
|
|||||||
Reference in New Issue
Block a user