Vertical-Slice-Architecture-diagram.excalidraw

==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==

Vertical Slice Architecture — Horizontal Layers vs Vertical Slices

Text Elements

Horizontal Layers (Coupled across features) ^layered-label

Presentation Layer ^layer-presentation Business Logic Layer ^layer-business Data Access Layer ^layer-data

Vertical Slices (Independent per feature) ^slices-label

Create Order ^slice1-label Handler ^slice1-handler Validator ^slice1-validator Repository ^slice1-repo

Get Order ^slice2-label Handler ^slice2-handler ReadModel ^slice2-readmodel Query ^slice2-query

Cancel Order ^slice3-label Handler ^slice3-handler Saga ^slice3-saga Repository ^slice3-repo2

Shared: Domain Model, Cross-Cutting Concerns (auth, logging) ^shared-label

Each slice owns its full stack: request -> handler -> data access. No shared service layer. ^annotation

%%

Drawing

{
  "type": "excalidraw",
  "version": 2,
  "source": "https://excalidraw.com",
  "elements": [
    {
      "id": "layered-title",
      "type": "text",
      "x": 40,
      "y": 20,
      "width": 320,
      "height": 24,
      "text": "Horizontal Layers (Coupled across features)",
      "fontSize": 15,
      "fontFamily": 1,
      "textAlign": "left",
      "verticalAlign": "top",
      "strokeColor": "#e03131",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "layer-presentation",
      "type": "rectangle",
      "x": 40,
      "y": 56,
      "width": 320,
      "height": 60,
      "strokeColor": "#868e96",
      "backgroundColor": "#f1f3f5",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "strokeStyle": "dashed",
      "opacity": 80
    },
    {
      "id": "layer-presentation-text",
      "type": "text",
      "x": 140,
      "y": 78,
      "width": 120,
      "height": 20,
      "text": "Presentation Layer",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "layer-business",
      "type": "rectangle",
      "x": 40,
      "y": 126,
      "width": 320,
      "height": 60,
      "strokeColor": "#868e96",
      "backgroundColor": "#f1f3f5",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "strokeStyle": "dashed",
      "opacity": 80
    },
    {
      "id": "layer-business-text",
      "type": "text",
      "x": 130,
      "y": 148,
      "width": 140,
      "height": 20,
      "text": "Business Logic Layer",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "layer-data",
      "type": "rectangle",
      "x": 40,
      "y": 196,
      "width": 320,
      "height": 60,
      "strokeColor": "#868e96",
      "backgroundColor": "#f1f3f5",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "strokeStyle": "dashed",
      "opacity": 80
    },
    {
      "id": "layer-data-text",
      "type": "text",
      "x": 130,
      "y": 218,
      "width": 140,
      "height": 20,
      "text": "Data Access Layer",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slices-title",
      "type": "text",
      "x": 440,
      "y": 20,
      "width": 320,
      "height": 24,
      "text": "Vertical Slices (Independent per feature)",
      "fontSize": 15,
      "fontFamily": 1,
      "textAlign": "left",
      "verticalAlign": "top",
      "strokeColor": "#2f9e44",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "band-presentation",
      "type": "rectangle",
      "x": 440,
      "y": 56,
      "width": 480,
      "height": 60,
      "strokeColor": "#dee2e6",
      "backgroundColor": "#f8f9fa",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 60
    },
    {
      "id": "band-presentation-label",
      "type": "text",
      "x": 448,
      "y": 78,
      "width": 100,
      "height": 20,
      "text": "Presentation",
      "fontSize": 11,
      "fontFamily": 1,
      "textAlign": "left",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 70
    },
    {
      "id": "band-business",
      "type": "rectangle",
      "x": 440,
      "y": 126,
      "width": 480,
      "height": 60,
      "strokeColor": "#dee2e6",
      "backgroundColor": "#f8f9fa",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 60
    },
    {
      "id": "band-business-label",
      "type": "text",
      "x": 448,
      "y": 148,
      "width": 100,
      "height": 20,
      "text": "Business",
      "fontSize": 11,
      "fontFamily": 1,
      "textAlign": "left",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 70
    },
    {
      "id": "band-data",
      "type": "rectangle",
      "x": 440,
      "y": 196,
      "width": 480,
      "height": 60,
      "strokeColor": "#dee2e6",
      "backgroundColor": "#f8f9fa",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 60
    },
    {
      "id": "band-data-label",
      "type": "text",
      "x": 448,
      "y": 218,
      "width": 100,
      "height": 20,
      "text": "Data Access",
      "fontSize": 11,
      "fontFamily": 1,
      "textAlign": "left",
      "verticalAlign": "middle",
      "strokeColor": "#868e96",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 70
    },
    {
      "id": "slice1",
      "type": "rectangle",
      "x": 460,
      "y": 56,
      "width": 140,
      "height": 200,
      "strokeColor": "#2f9e44",
      "backgroundColor": "#d3f9d8",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "opacity": 90
    },
    {
      "id": "slice1-label",
      "type": "text",
      "x": 470,
      "y": 64,
      "width": 120,
      "height": 20,
      "text": "Create Order",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#2f9e44",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice1-handler",
      "type": "text",
      "x": 490,
      "y": 100,
      "width": 80,
      "height": 20,
      "text": "Handler",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#2f9e44",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice1-validator",
      "type": "text",
      "x": 490,
      "y": 166,
      "width": 80,
      "height": 20,
      "text": "Validator",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#2f9e44",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice1-repo",
      "type": "text",
      "x": 490,
      "y": 228,
      "width": 80,
      "height": 20,
      "text": "Repository",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#2f9e44",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice2",
      "type": "rectangle",
      "x": 614,
      "y": 56,
      "width": 140,
      "height": 200,
      "strokeColor": "#1971c2",
      "backgroundColor": "#d0ebff",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "opacity": 90
    },
    {
      "id": "slice2-label",
      "type": "text",
      "x": 624,
      "y": 64,
      "width": 120,
      "height": 20,
      "text": "Get Order",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#1971c2",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice2-handler",
      "type": "text",
      "x": 644,
      "y": 100,
      "width": 80,
      "height": 20,
      "text": "Handler",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#1971c2",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice2-readmodel",
      "type": "text",
      "x": 638,
      "y": 166,
      "width": 90,
      "height": 20,
      "text": "ReadModel",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#1971c2",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice2-query",
      "type": "text",
      "x": 644,
      "y": 228,
      "width": 80,
      "height": 20,
      "text": "Query",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#1971c2",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice3",
      "type": "rectangle",
      "x": 768,
      "y": 56,
      "width": 140,
      "height": 200,
      "strokeColor": "#e67700",
      "backgroundColor": "#ffe8cc",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "opacity": 90
    },
    {
      "id": "slice3-label",
      "type": "text",
      "x": 778,
      "y": 64,
      "width": 120,
      "height": 20,
      "text": "Cancel Order",
      "fontSize": 13,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#e67700",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice3-handler",
      "type": "text",
      "x": 798,
      "y": 100,
      "width": 80,
      "height": 20,
      "text": "Handler",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#e67700",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice3-saga",
      "type": "text",
      "x": 798,
      "y": 166,
      "width": 80,
      "height": 20,
      "text": "Saga",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#e67700",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "slice3-repo2",
      "type": "text",
      "x": 798,
      "y": 228,
      "width": 80,
      "height": 20,
      "text": "Repository",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#e67700",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "shared-box",
      "type": "rectangle",
      "x": 440,
      "y": 270,
      "width": 480,
      "height": 50,
      "strokeColor": "#495057",
      "backgroundColor": "#f8f9fa",
      "fillStyle": "solid",
      "strokeWidth": 2,
      "roughness": 1,
      "strokeStyle": "dashed",
      "opacity": 100
    },
    {
      "id": "shared-text",
      "type": "text",
      "x": 450,
      "y": 283,
      "width": 460,
      "height": 24,
      "text": "Shared: Domain Model, Cross-Cutting Concerns (auth, logging)",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#495057",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "annotation",
      "type": "text",
      "x": 40,
      "y": 340,
      "width": 880,
      "height": 20,
      "text": "Each slice owns its full stack: request -> handler -> data access. No shared service layer.",
      "fontSize": 12,
      "fontFamily": 1,
      "textAlign": "center",
      "verticalAlign": "middle",
      "strokeColor": "#495057",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "opacity": 100
    },
    {
      "id": "divider",
      "type": "line",
      "x": 400,
      "y": 20,
      "width": 0,
      "height": 300,
      "strokeColor": "#ced4da",
      "backgroundColor": "transparent",
      "fillStyle": "solid",
      "strokeWidth": 1,
      "roughness": 1,
      "strokeStyle": "dashed",
      "opacity": 80,
      "points": [[0, 0], [0, 300]]
    }
  ],
  "appState": {
    "gridSize": null,
    "viewBackgroundColor": "#ffffff"
  },
  "files": {}
}

%%