namespace Docs.Examples open FSharp.ViewEngine open Acme.Components open type Html module Architecture = let navigation = [ "architecture", "System context" "architecture/solution", "Overview" "architecture/solution/domain", "Ledger.Domain" "architecture/solution/application", "Ledger.Application" "architecture/solution/components", "Acme.Components" "architecture/solution/server", "Ledger.Server" ] let href page = "/examples/specification/"+page let private click node page = $" click {node} href \"{href page}\" _self" let private graph lines = String.concat "\n" (lines @ [ " classDef person fill:#f3f4f6,stroke:#6b7280,color:#111827" " classDef system fill:#059669,stroke:#047857,color:#ffffff,stroke-width:2px" " classDef project fill:#ecfdf5,stroke:#059669,color:#064e3b" " classDef executable fill:#d1fae5,stroke:#047857,color:#064e3b,stroke-width:2px" " classDef navigable stroke:#059669,stroke-width:3px" ]) let private diagram label source = div { _role "region"; _ariaLabel label; _tabindex 0; _class "min-w-0 overflow-x-auto rounded-xl focus-visible:outline-2 focus-visible:outline-[var(--fve-brand-ring)]"; Mermaid.create source |> Mermaid.withC4 |> Mermaid.render } let private rules (items:string list) = Layout.section "Rules" (Layout.prose (ul { for item in items do li { item } })) let private context = graph [ "flowchart LR" " Operator[\"Financial operator
Person
Reviews accounts and activity\"]:::person" " Developer[\"Application developer
Person
Reviews workflows and HTTP contracts\"]:::person" " Ledger[\"Ledger
Software system
Financial application, executable specification and API reference\"]:::system" " Operator -->|Uses financial pages| Ledger" " Developer -->|Reviews specification and cURL examples| Ledger" " class Ledger navigable" click "Ledger" "architecture/solution" ] let private solution = graph [ "flowchart LR" " Browser[\"Browser
External runtime\"]:::person" " subgraph Product[\"Ledger solution\"]" " Server[\"Ledger.Server
Executable F# project
HTML, forms, Specification and API reference\"]:::executable" " Application[\"Ledger.Application
F# library project
Submission validation and selection review\"]:::project" " Domain[\"Ledger.Domain
F# library project
Financial and workspace facts, balance and deletion rules\"]:::project" " Components[\"Acme.Components
Source-installed F# project
Reusable HTML controls and presentation\"]:::project" " Server -->|Project reference| Application" " Server -->|Project reference| Components" " Application -->|Project reference| Domain" " end" " Browser -.->|HTTP GET and POST| Server" " class Server,Application,Domain,Components navigable" click "Server" "architecture/solution/server" click "Application" "architecture/solution/application" click "Domain" "architecture/solution/domain" click "Components" "architecture/solution/components" ] let private contracts heading (description:string) (dependencies:string list) code requirements = div { _class "grid gap-8" Layout.section "Responsibility" (Layout.prose (p { description })) Layout.section "Dependencies" (Layout.prose (ul { for dependency in dependencies do li { dependency } })) Layout.section heading (CodeBlock.create "fsharp" code |> CodeBlock.render) rules requirements } let render page = match page with | "architecture" -> div { _class "grid gap-8" Layout.section "System context" (diagram "Ledger system context" context) Layout.section "System boundary" (Layout.prose (p { "Ledger provides a financial workspace, an executable specification and a cURL API reference over one shared set of USD account and transaction fixtures." })) rules ["The three surfaces SHALL describe the same accounts, transactions and workspace contexts.";"Financial submissions SHALL validate finite outcomes without retaining private values or mutating the seeded records.";"The Ledger node SHALL open the Solution overview."] } | "architecture/solution" -> div { _class "grid gap-8" Layout.section "Projects" (diagram "Ledger project dependencies and HTTP boundary" solution) Layout.section "Project inventory" (DescriptionList.create [ DescriptionListItem.create "Ledger.Domain" (Layout.link (href "architecture/solution/domain") "Financial types and invariants") DescriptionListItem.create "Ledger.Application" (Layout.link (href "architecture/solution/application") "Validation operations and outcomes") DescriptionListItem.create "Acme.Components" (Layout.link (href "architecture/solution/components") "Installed HTML building blocks") DescriptionListItem.create "Ledger.Server" (Layout.link (href "architecture/solution/server") "HTTP hosting and the three rendered surfaces") ] |> DescriptionList.render) rules ["Compile-time references SHALL flow Server → Application → Domain; Server SHALL also reference Components.";"Domain and Application SHALL remain independent of Giraffe, HTML and browser state.";"Ledger.Server SHALL be the only executable project. Example.fsproj SHALL compile that assembly.";"Solid relationships SHALL represent project references; the dashed relationship SHALL represent HTTP requests."] } | "architecture/solution/domain" -> contracts "Financial types" "Domain/Domain.fs owns immutable financial fixtures, organization/environment/ledger context, balance calculations and deletion eligibility." ["System and FSharp.Core only."] "type Account = { id:int; name:string; accountType:AccountType; currency:string; openingBalance:decimal }\ntype Transaction = { id:int; date:DateOnly; description:string; accountId:int; amount:decimal; status:TransactionStatus }\ntype Workspace = { organization:Organization; environment:Environment; ledger:Ledger }\n// balance : Account -> decimal\n// canDeleteAccount : int -> bool" ["Account balances SHALL equal opening balance plus the account's signed transactions.";"Deletion eligibility SHALL require an existing account, zero balance and no transactions.";"Every ledger SHALL belong to its selected organization. Unknown context values SHALL resolve to available fixtures."] | "architecture/solution/application" -> contracts "Operations" "UseCases/Operations.fs validates account, organization and profile submissions, checks selection keys and evaluates account deletion." ["Ledger.Domain."] "// validateAccount : ValidateAccountRequest -> Result\n// reviewSelection : ReviewSelectionRequest -> Result\n// validateDeletion : ValidateDeletionRequest -> Result\n// validateOrganization : ValidateOrganizationRequest -> Result\n// validateProfile : ValidateProfileRequest -> Result" ["Operations SHALL return typed success or validation errors without HTTP status codes or rendered HTML.";"Account names SHALL contain 1–80 characters and be unique ignoring case.";"Selection review SHALL reject empty or unknown keys.";"Validation SHALL neither mutate seeded data nor retain submitted values."] | "architecture/solution/components" -> contracts "Composition" "Components/Acme.Components.fsproj contains independently installed source for the controls used by the three surfaces." ["FSharp.ViewEngine and each selected component's declared dependencies."] "PageTopBar.create ()\n|> PageTopBar.withBrand brand\n|> PageTopBar.withContent breadcrumbs\n|> PageTopBar.withActions actions\n|> PageTopBar.render\n\nSideNav.create id label header sections\n|> SideNav.withoutHeader\n|> SideNav.render resolve" ["Components SHALL own their presentation, accessibility and local interaction behavior.";"The consumer SHALL own financial content, routes, theme and workflow state.";"The full-width PageTopBar SHALL own the shell's shared header height and separator."] | "architecture/solution/server" -> contracts "HTTP surfaces" "Example.fsproj builds Ledger.Server. Model.fs and Routing.fs own URLs; Layout/Application own shared product HTML; Specification/Architecture and ApiDocumentation render the other surfaces; Hosting.fs adapts native forms to Application operations." ["Ledger.Application and Acme.Components; Giraffe at the HTTP edge."] "// GET /examples/application/accounts\n// GET /examples/application/accounts/{id}\n// GET /examples/specification/accounts/view-accounts\n// GET /examples/specification/accounts/view-account?resource=101\n// POST /examples/application/accounts/new\n// POST /examples/application/accounts/{id}/edit\n// POST /examples/application/accounts/{id}/delete\n// GET /examples/api-documentation" ["GET requests SHALL render the product selected by the URL.";"Specification App-mode links SHALL retain workflow, fixture identity, workspace and docking context.";"POST requests SHALL accept same-origin URL-encoded bodies of at most 64,000 bytes and map typed operation results to finite redirects.";"Browser form endpoints and illustrative JSON API contracts SHALL remain distinguishable."] | _ -> invalidArg (nameof page) "Choose a registered architecture page."