Design functional software architectures using Functional Declarative Design (FDD) methodology. NEVER auto-invoke this skill. Only use when the user explicitly asks for functional architecture design (e.g. "/func-arch", "design the architecture functionally", "functional architecture for ..."). When invoked, the user MUST provide specific context about what to design — do not proceed without it.
A methodology for designing software architectures rooted in Functional Declarative Design (FDD), based on Alexander Granin's Functional Design and Architecture. This skill guides you through designing modular, testable, low-complexity architectures using functional principles — applicable in any language that supports functional patterns.
IMPORTANT: This skill is never auto-invoked. Only activate when the user explicitly requests functional architecture design, and only when they provide specific context (domain, requirements, constraints). If context is missing, ask for it before proceeding.
The user should tell you:
If any of these are missing, ask before proceeding.
Before designing anything, understand the domain:
Design the architecture using FDD's layered approach. Read references/methodology.md for the full pattern catalog.
Every functional application should be decomposed into these layers:
| Layer | Responsibility | Purity |
|---|---|---|
| Domain Model | ADTs, domain types, domain logic, DSLs | Pure |
| Business Logic | Scenarios, workflows, orchestration using domain DSLs | Pure (ideally) |
| Service / Application | Configuration, initialization, lifecycle, threading, logging | Impure |
| Persistence | Data access abstractions, storage declarations | Pure interface, impure implementation |
| Interoperability | Event handling, reactive logic, cross-layer communication | Impure |
| Presentation | GUI, CLI, API endpoints, I/O | Impure |
The pure layer declares behavior but never evaluates it impurely. The impure layer interprets pure declarations and interacts with the outside world. Keep the impure layer as thin as possible.
Select the right pattern based on the project's needs. Present trade-offs to the user:
| Pattern | Complexity | Testability | When to Use |
|---|---|---|---|
| Service Handle | Low | High (swap implementations via records/objects of functions) | Simple services, pragmatic codebases |
| ReaderT / Reader | Medium | High (inject dependencies via environment) | When you need implicit dependency injection |
| Free Monad | High | Very High (interpret programs differently for test/prod) | Complex DSLs, need full introspection of programs |
| Final Tagless / mtl | High | High (abstract over effect type) | Haskell/Scala ecosystems, composable effects |
| Effect Systems | Medium-High | High | Modern effect libraries (ZIO, Polysemy, Arrow) |
| GADT | High | High | When you need type-safe, extensible command sets |
For most Kotlin/TypeScript projects, Service Handle or Reader-based patterns give the best pragmatism-to-power ratio.
For each major component:
Present the architecture incrementally (following the brainstorm skill's pattern):
After each section, ask: "Does this look right so far?"
Once the design is accepted:
For the complete pattern catalog, design heuristics, and language-specific examples, read:
references/methodology.md
references/examples.md
Always consult references/methodology.md when:
Always consult references/examples.md when: