← ExamplesSpecification

Template source

Complete consumer-owned files. Start with Setup.md; download the project and source files together. The catalog viewer bar is not included.

Architecture.fs

Download file
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<br/>Person<br/>Reviews accounts and activity\"]:::person"
        "  Developer[\"Application developer<br/>Person<br/>Reviews workflows and HTTP contracts\"]:::person"
        "  Ledger[\"Ledger<br/>Software system<br/>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<br/>External runtime\"]:::person"
        "  subgraph Product[\"Ledger solution\"]"
        "    Server[\"Ledger.Server<br/>Executable F# project<br/>HTML, forms, Specification and API reference\"]:::executable"
        "    Application[\"Ledger.Application<br/>F# library project<br/>Submission validation and selection review\"]:::project"
        "    Domain[\"Ledger.Domain<br/>F# library project<br/>Financial and workspace facts, balance and deletion rules\"]:::project"
        "    Components[\"Acme.Components<br/>Source-installed F# project<br/>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<ValidateAccountResponse, ValidateAccountError>\n// reviewSelection : ReviewSelectionRequest -> Result<ReviewSelectionResponse, ReviewSelectionError>\n// validateDeletion : ValidateDeletionRequest -> Result<ValidateDeletionResponse, ValidateDeletionError>\n// validateOrganization : ValidateOrganizationRequest -> Result<ValidateOrganizationResponse, ValidateOrganizationError>\n// validateProfile : ValidateProfileRequest -> Result<ValidateProfileResponse, ValidateProfileError>"
                ["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."

Install the listed components, compile your Tailwind stylesheet, and provide the assets configured in Layout.fs.

Financial specification