Back to index

Layered Architecture

Stack
Generates documents
Layered Architecture

Seen as a design

Take it with you

Copy and paste, or save a file and hand it over. For a machine with no network, one “Everything” file is enough.

Everything

layered-architecture-everything.md

Everything in this panel, including the contents behind each URL, in one Markdown file

Prompt

1.4 KB
Build an HTML document with "Layered Architecture". This layout runs, so there is no composition to rebuild — what it needs is content.

## What this layout does
Layered Architecture with explicit source values and reading conditions.

## What you have to supply
- Provide the source values, labels and decision conditions described by the schema.

## The shape of the input
- `title` — string — 結論を伝える見出し
- `subtitle` — string? — 出典・時点・母集団や仮定
- `layers` — array (id, label, responsibility, components) — 上から下に並べる層・責務・要素
- `boundary` — string — 層間の依存ルール

schema.ts holds the machine-checkable validation. Ask rather than guess when an input is missing.

## When not to use it
- Do not compare values with incompatible units, populations, or assumptions.

## How to build it
Put the input in a JSON file and run:

```bash
node scripts/render.mjs layered-architecture <input.json> --theme ink -o out.html
```

The output is one self-contained file and fetches nothing at render time.

## The source
- https://design-vault.pages.dev/designs/layered-architecture/source/template.tsx
- https://design-vault.pages.dev/designs/layered-architecture/source/schema.ts
- https://design-vault.pages.dev/designs/layered-architecture/source/sample-data.json
- https://design-vault.pages.dev/designs/layered-architecture/sample.html

Files

Template, input schema, sample data and metadata. This document was generated from those four. The generated document

template.tsx

6.7 KB

/designs/layered-architecture/source/template.tsx

Show contents
import type { ReactNode } from "react";
import type { Props } from "./schema";
const colors = ["var(--color-tertiary)", "var(--color-primary)", "var(--color-secondary)", "var(--color-accent-violet)"];
function Notes({children}:{children:ReactNode}) { return <details><summary>データ・判断条件を確認</summary>{children}</details>; }

const CSS = String.raw`
.asset{font-family:var(--font-sans);color:var(--color-primary);background:var(--color-surface);max-width:var(--container-slide);margin:auto;padding:3rem 2rem;line-height:1.6;font-size:var(--text-body-sm)}
.asset *{box-sizing:border-box}.asset header{margin-bottom:2rem}.asset .eyebrow{font-family:var(--font-mono);font-size:var(--text-label);letter-spacing:.08em;color:var(--color-secondary);margin-bottom:.75rem}.asset h1{font-size:var(--text-h1);font-weight:700;line-height:1.35;letter-spacing:-.025em;max-width:32em}.asset h2{font-size:var(--text-h3);font-weight:600;margin-bottom:.75rem}.asset h3{font-size:var(--text-body-md);font-weight:600}.asset .subtitle,.asset .muted{color:var(--color-secondary)}.asset .subtitle{margin-top:.75rem}.asset .number{font-variant-numeric:tabular-nums;font-family:var(--font-mono)}.asset .hero-number{font-size:var(--text-display);line-height:1.2;font-weight:700;letter-spacing:-.04em}.asset .callout{border-left:.25rem solid var(--color-tertiary);padding:1rem 1.5rem;background:var(--color-surface-subtle);margin-bottom:1.5rem}.asset .grid2{display:grid;grid-template-columns:1fr 1fr;gap:1.5rem}.asset .grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1.5rem}.asset .panel{padding:1.5rem;background:var(--color-surface-subtle);border-radius:var(--radius-md);min-width:0}.asset .rule{border-top:1px solid var(--color-outline);padding-top:1rem;margin-top:1rem}.asset .pill{display:inline-block;padding:.25rem .75rem;border:1px solid var(--color-outline);border-radius:var(--radius-full);font-size:var(--text-label)}.asset .focus{background:var(--color-tertiary-container);color:var(--color-on-tertiary-container)}.asset .dark{background:var(--color-primary);color:var(--color-on-primary)}.asset .dark .muted{color:var(--color-on-primary-container)}.asset .row{display:grid;grid-template-columns:12rem minmax(0,1fr) 6rem;gap:1rem;align-items:center;padding:.75rem 0;border-bottom:1px solid var(--color-outline)}.asset .row>*{min-width:0}.asset .track{height:1rem;position:relative;background:var(--color-neutral);border-radius:var(--radius-sm)}.asset .bar{height:100%;background:var(--color-tertiary);border-radius:var(--radius-sm)}.asset .track.tall{height:2rem}.asset .flex{display:flex;gap:1rem;align-items:center;justify-content:space-between;flex-wrap:wrap}.asset .stack>*+*{margin-top:1rem}.asset ul{list-style:disc;padding-left:1.25rem}.asset li+li{margin-top:.5rem}.asset .plot{display:block;width:100%;height:auto;overflow:visible}.asset .plot text{font-family:var(--font-sans);font-size:14px;fill:var(--color-secondary)}.asset .plot .ink{fill:var(--color-primary)}.asset .plot .axis{stroke:var(--color-outline);stroke-width:1}.asset .plot .accent{stroke:var(--color-tertiary);fill:none;stroke-width:3}.asset .legend{display:flex;gap:1.5rem;flex-wrap:wrap;font-size:var(--text-label);margin-bottom:1rem}.asset .legend b{display:inline-block;width:.75rem;height:.75rem;margin-right:.5rem}.asset .ledger{border-collapse:collapse;width:100%;font-variant-numeric:tabular-nums}.asset .ledger caption{text-align:left;font-weight:600;margin:1rem 0}.asset .ledger th{font-weight:600;color:var(--color-secondary);text-align:left}.asset .ledger th,.asset .ledger td{padding:.75rem 1rem;border-bottom:1px solid var(--color-outline);vertical-align:top;overflow-wrap:anywhere}.asset .ledger th:first-child,.asset .ledger td:first-child{padding-left:0}.asset .ledger th:last-child,.asset .ledger td:last-child{padding-right:0}.asset .ledger tbody tr:hover{background:var(--color-surface-subtle)}.asset details{border-top:1px solid var(--color-outline);margin-top:1.5rem;padding-top:1rem}.asset summary{cursor:pointer;color:var(--color-secondary);padding:.5rem 0;min-height:2.75rem}.asset summary:focus-visible{outline:2px solid var(--color-tertiary);outline-offset:.25rem}.asset .small{font-size:var(--text-label)}.asset .mobile-only{display:none}.asset .horizontal-list{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem}.asset .board-column{border-top:.25rem solid var(--color-primary);padding-top:1rem;min-width:0}.asset .board-item{padding:1rem 0;border-bottom:1px solid var(--color-outline)}.asset .board-item p{margin-top:.5rem}.asset .statement{font-size:var(--text-h2);line-height:1.5;font-weight:600}.asset .meta-line{margin-top:.75rem;font-size:var(--text-label);color:var(--color-secondary)}.asset pre{white-space:pre-wrap;overflow-wrap:anywhere}.asset footer{margin-top:2rem;font-size:var(--text-label);color:var(--color-secondary)}
@media(max-width:640px){.asset{padding:1.5rem 1rem}.asset h1{font-size:var(--text-h2)}.asset header{margin-bottom:1.5rem}.asset .grid2,.asset .grid3,.asset .horizontal-list{grid-template-columns:1fr}.asset .row{grid-template-columns:minmax(0,1fr) 5rem;gap:.5rem}.asset .row>:first-child{grid-column:1/-1}.asset .desktop-only{display:none!important}.asset .mobile-only{display:block}.asset .panel{padding:1rem}.asset .ledger,.asset .ledger tbody,.asset .ledger tr,.asset .ledger td{display:block}.asset .ledger thead{display:none}.asset .ledger tr{padding:.75rem 0;border-bottom:1px solid var(--color-outline)}.asset .ledger td{display:grid;grid-template-columns:7rem minmax(0,1fr);gap:.75rem;border:0;padding:.25rem 0!important}.asset .ledger td:before{content:attr(data-label);color:var(--color-secondary);font-size:var(--text-label)}.asset .hero-number{font-size:var(--text-h1)}}
@media(min-width:641px){.asset-layered-architecture .panel{padding:.75rem 1rem}.asset-layered-architecture h2{margin-bottom:0}}
@media print{.asset{max-width:none;padding:1rem}.asset details>*{display:block}.asset summary{display:none}}
`;

export default function Template(d: Props) {

return <section className="asset asset-layered-architecture"><style>{CSS}</style><header><p className="eyebrow">LAYERED ARCHITECTURE</p><h1>{d.title}</h1>{d.subtitle && <p className="subtitle">{d.subtitle}</p>}</header>
<div className="stack">{d.layers.map((l,i)=><article className="grid2 panel" key={l.id} style={{borderLeft:".25rem solid "+(i===0?colors[0]:"var(--color-outline)")}}><div><span className="small muted">{l.id}</span><h2>{l.label}</h2><p>{l.responsibility}</p></div><div className="flex" style={{justifyContent:"flex-start"}}>{l.components.map((c,j)=><span key={j} className="pill">{c}</span>)}</div></article>)}</div><Notes><h3>依存のルール</h3><p>{d.boundary}</p></Notes>
<footer>層の並びは責務の整理。物理配置や通信経路と混同しない。</footer></section>;
}

schema.ts

794 B

/designs/layered-architecture/source/schema.ts

Show contents
import { z } from "zod";
const text = z.string().trim().min(1).max(160);
const short = z.string().trim().min(1).max(48);
const value = z.number().finite();
const positive = value.positive();
const unique = (values: string[]) => new Set(values).size === values.length;

export const schema = z.object({
  title: text.describe("結論を伝える見出し"),
  subtitle: text.optional().describe("出典・時点・母集団や仮定"),
  layers:z.array(z.object({id:short,label:short,responsibility:text,components:z.array(short).min(1).max(5)}).strict()).min(3).max(5).describe("上から下に並べる層・責務・要素"),boundary:text.describe("層間の依存ルール"),
}).strict().refine(d=>unique(d.layers.map(l=>l.id)),"層IDは一意");
export type Props = z.infer<typeof schema>;

meta.yaml

1.3 KB

/designs/layered-architecture/source/meta.yaml

Show contents
id: layered-architecture
name:
  ja: 責務を分ける階層構成
  en: Layered Architecture
purpose:
  ja: システムを責務の層に分け、各層の境界と構成要素を示す。
  en: Layered Architecture with explicit source values and reading conditions.
status: published
still: ./still.png
category: specification
catalogOrder: 620
jobs:
  - explain-structure
useCases:
  - org
layoutKinds:
  - stack
tags:
  - layered-architecture
  - specification
  - offline
  - responsive
whenToUse:
  - ja: システムを責務の層に分け、各層の境界と構成要素を示す。
    en: Use to communicate layered architecture.
avoidWhen:
  - ja: 同じ条件・単位で比較できないデータには使わない
    en: Do not compare values with incompatible units, populations, or assumptions.
requiredInputs:
  - ja: 層の並びは責務の整理。物理配置や通信経路と混同しない。
    en: Provide the source values, labels and decision conditions described by the schema.
relatedLayouts:
  - system-landscape-map
  - integration-contract-catalog
dataSchema:
  title: string — 結論を伝える見出し
  subtitle: string? — 出典・時点・母集団や仮定
  layers: array (id, label, responsibility, components) — 上から下に並べる層・責務・要素
  boundary: string — 層間の依存ルール
sources: []

sample-data.json

1.0 KB

/designs/layered-architecture/source/sample-data.json

Show contents
{
  "title": "受付・業務・連携・記録の責務を分離する",
  "subtitle": "架空サンプル|レイアウトの使用例",
  "layers": [
    {
      "id": "L1",
      "label": "利用者接点",
      "responsibility": "入力と結果の提示",
      "components": [
        "申込画面",
        "進捗照会"
      ]
    },
    {
      "id": "L2",
      "label": "業務サービス",
      "responsibility": "業務ルールの適用",
      "components": [
        "受付処理",
        "審査依頼"
      ]
    },
    {
      "id": "L3",
      "label": "連携",
      "responsibility": "外部契約の吸収",
      "components": [
        "APIゲートウェイ",
        "通知アダプター"
      ]
    },
    {
      "id": "L4",
      "label": "データ",
      "responsibility": "記録の保存と参照",
      "components": [
        "申込DB",
        "監査ログ"
      ]
    }
  ],
  "boundary": "画面から外部サービスへ直接接続せず、業務・連携層を経由する"
}