feat: update documentation and component demos for backend layered architecture

- Add new LanguageScopeDemo component for backend languages overview
- Refactor and simplify existing demo components (ControllerLayerDemo, DtoFlowDemo, DependencyDirectionDemo)
- Update .gitignore to exclude .claude/skills directory
- Modify backend-related sections in documentation from "后端与全栈" to "后端开发"
- Add new backend layered architecture demo components (CleanArchitectureDemo, DependencyDirectionDemo)
- Improve documentation structure and content for stage-3 core skills
- Fix component initialization timing in CompileVsInterpretDemo and RateLimiterDemo
- Add design style prompt reference in frontend documentation
This commit is contained in:
sanbuphy
2026-03-01 12:28:47 +08:00
parent d8eb93663d
commit dc8b5773f1
22 changed files with 2660 additions and 5288 deletions
@@ -1,228 +1,78 @@
<template>
<div class="clean-architecture-demo">
<div class="demo-header">
<h4>🏗 整洁架构Clean Architecture与分层架构</h4>
<p class="subtitle">
分层架构是整洁架构的基础理解两者的关系有助于构建更灵活的系统
</p>
<div class="clean-arch-demo">
<div class="header">
<div class="title">整洁架构与分层架构对比</div>
<div class="subtitle">分层架构是整洁架构的基础理解两者关系有助于构建更灵活的系统</div>
</div>
<!-- 架构对比 -->
<div class="architecture-comparison">
<div class="comparison-tabs">
<button
v-for="tab in tabs"
:key="tab.id"
:class="['tab-btn', { active: currentTab === tab.id }]"
@click="currentTab = tab.id"
>
{{ tab.name }}
</button>
<div class="tabs">
<button
v-for="t in tabs" :key="t.id"
:class="['tab', { active: current === t.id }]"
@click="current = t.id"
>{{ t.name }}</button>
</div>
<div v-if="current === 'layered'" class="panel">
<div class="arch-layers">
<div v-for="l in layeredLayers" :key="l.name" :class="['arch-layer', l.cls]">
<strong>{{ l.name }}</strong> <span>{{ l.desc }}</span>
</div>
</div>
<div class="traits">
<strong>传统分层架构特点</strong>
<ul>
<li>垂直依赖上层直接依赖下层</li>
<li>简单直观结构清晰易于理解</li>
<li>适合中小型项目快速开发上手简单</li>
<li>潜在问题底层变更可能影响上层</li>
</ul>
</div>
</div>
<div class="comparison-content">
<!-- 传统分层架构 -->
<div
v-if="currentTab === 'layered'"
class="tab-panel"
>
<div class="arch-diagram layered">
<div class="layer-box controller">
<div class="layer-title">
Controller
</div>
<div class="layer-desc">
接收请求参数校验
</div>
</div>
<div class="arrow down">
依赖
</div>
<div class="layer-box service">
<div class="layer-title">
Service
</div>
<div class="layer-desc">
业务逻辑事务管理
</div>
</div>
<div class="arrow down">
依赖
</div>
<div class="layer-box repository">
<div class="layer-title">
Repository
</div>
<div class="layer-desc">
数据访问ORM 映射
</div>
</div>
<div class="arrow down">
依赖
</div>
<div class="layer-box domain">
<div class="layer-title">
Domain
</div>
<div class="layer-desc">
实体定义业务规则
</div>
</div>
</div>
<div class="arch-characteristics">
<h5>📌 传统分层架构特点</h5>
<ul>
<li><strong>垂直依赖</strong>上层直接依赖下层依赖方向从上到下</li>
<li><strong>简单直观</strong>结构清晰易于理解和实现</li>
<li><strong>适合中小型项目</strong>快速开发上手简单</li>
<li><strong>潜在问题</strong>底层变更可能影响上层循环依赖风险</li>
</ul>
</div>
<div v-else-if="current === 'clean'" class="panel">
<div class="clean-layers">
<div v-for="l in cleanLayers" :key="l.name" :class="['arch-layer', l.cls]">
<strong>{{ l.name }}</strong> <span>{{ l.items }}</span>
</div>
</div>
<div class="dep-rule">依赖方向外层 内层内层不知道外层的存在</div>
<div class="traits">
<strong>整洁架构特点</strong>
<ul>
<li>依赖倒置依赖方向从外到内通过接口隔离</li>
<li>领域为核心业务逻辑位于中心独立于框架</li>
<li>可测试性强核心业务可脱离框架单元测试</li>
<li>技术无关可轻松切换数据库框架等</li>
</ul>
</div>
</div>
<!-- 整洁架构 -->
<div
v-else-if="currentTab === 'clean'"
class="tab-panel"
>
<div class="arch-diagram clean">
<div class="clean-layers">
<div class="clean-layer framework">
<div class="layer-name">
框架与驱动层
</div>
<div class="layer-items">
Web / DB / UI / 外部接口
</div>
</div>
<div class="clean-layer interface">
<div class="layer-name">
接口适配层
</div>
<div class="layer-items">
Controller / Gateway / Presenter
</div>
</div>
<div class="clean-layer application">
<div class="layer-name">
应用层
</div>
<div class="layer-items">
Service / UseCase / DTO
</div>
</div>
<div class="clean-layer domain">
<div class="layer-name">
领域层核心
</div>
<div class="layer-items">
Entity / ValueObject / DomainService
</div>
</div>
</div>
<div class="dependency-rule">
<div class="rule-arrow">
<span class="arrow-line" />
<span class="arrow-head"> 依赖方向</span>
</div>
<div class="rule-text">
外层依赖内层内层不依赖外层
</div>
</div>
</div>
<div class="arch-characteristics">
<h5>📌 整洁架构特点</h5>
<ul>
<li><strong>依赖倒置</strong>依赖方向从外到内通过接口隔离</li>
<li><strong>领域为核心</strong>业务逻辑位于中心独立于框架</li>
<li><strong>可测试性强</strong>核心业务可脱离框架进行单元测试</li>
<li><strong>技术无关</strong>可轻松切换数据库框架等外部技术</li>
</ul>
</div>
<div v-else class="panel">
<table>
<thead><tr><th>特性</th><th>传统分层</th><th>整洁架构</th></tr></thead>
<tbody>
<tr v-for="r in compareRows" :key="r.feature">
<td>{{ r.feature }}</td><td>{{ r.layered }}</td><td>{{ r.clean }}</td>
</tr>
</tbody>
</table>
<div class="rec-grid">
<div class="rec-card">
<strong>选择传统分层当...</strong>
<ul>
<li>项目规模较小业务简单</li>
<li>团队对 DDD 不熟悉</li>
<li>需要快速上线验证市场</li>
</ul>
</div>
<!-- 对比总结 -->
<div
v-else
class="tab-panel"
>
<div class="comparison-table">
<table>
<thead>
<tr>
<th>特性</th>
<th>传统分层架构</th>
<th>整洁架构</th>
</tr>
</thead>
<tbody>
<tr>
<td>依赖方向</td>
<td>从上到下</td>
<td>从外到内</td>
</tr>
<tr>
<td>核心业务位置</td>
<td>Service </td>
<td>Domain 中心</td>
</tr>
<tr>
<td>框架依赖</td>
<td>较深 Spring</td>
<td>较浅通过接口隔离</td>
</tr>
<tr>
<td>可测试性</td>
<td>需要集成测试</td>
<td>核心可单元测试</td>
</tr>
<tr>
<td>学习曲线</td>
<td>平缓</td>
<td>较陡</td>
</tr>
<tr>
<td>适用场景</td>
<td>中小型项目快速迭代</td>
<td>大型复杂业务长期维护</td>
</tr>
</tbody>
</table>
</div>
<div class="recommendation">
<h5>💡 选型建议</h5>
<div class="rec-grid">
<div class="rec-card">
<div class="rec-title">
选择传统分层架构当...
</div>
<ul>
<li>项目规模较小业务相对简单</li>
<li>团队对 DDD 不熟悉</li>
<li>需要快速上线验证市场</li>
<li>技术栈相对固定</li>
</ul>
</div>
<div class="rec-card recommended">
<div class="rec-title">
选择整洁架构当...
</div>
<ul>
<li>业务复杂领域模型丰富</li>
<li>需要长期维护和演进</li>
<li>需要频繁切换技术栈</li>
<li>团队有较强的设计能力</li>
</ul>
<div class="rec-badge">
推荐
</div>
</div>
</div>
</div>
<div class="rec-card recommended">
<strong>选择整洁架构当...</strong>
<ul>
<li>业务复杂领域模型丰富</li>
<li>需要长期维护和演进</li>
<li>需要频繁切换技术栈</li>
</ul>
</div>
</div>
</div>
@@ -232,358 +82,91 @@
<script setup>
import { ref } from 'vue'
const currentTab = ref('layered')
const current = ref('layered')
const tabs = [
{ id: 'layered', name: '传统分层' },
{ id: 'clean', name: '整洁架构' },
{ id: 'comparison', name: '对比总结' }
{ id: 'compare', name: '对比总结' }
]
const layeredLayers = [
{ name: 'Controller 层', desc: '接收请求、参数校验', cls: 'green' },
{ name: 'Service 层', desc: '业务逻辑、事务管理', cls: 'orange' },
{ name: 'Repository 层', desc: '数据访问、ORM 映射', cls: 'blue' },
{ name: 'Domain 层', desc: '实体定义、业务规则', cls: 'teal' }
]
const cleanLayers = [
{ name: '领域层(核心)', items: 'Entity / ValueObject / DomainService', cls: 'teal' },
{ name: '应用层', items: 'Service / UseCase / DTO', cls: 'orange' },
{ name: '接口适配层', items: 'Controller / Gateway / Presenter', cls: 'blue' },
{ name: '框架与驱动层', items: 'Web / DB / UI / 外部接口', cls: 'gray' }
]
const compareRows = [
{ feature: '依赖方向', layered: '从上到下', clean: '从外到内' },
{ feature: '核心业务位置', layered: 'Service 层', clean: 'Domain 层(中心)' },
{ feature: '框架依赖', layered: '较深', clean: '较浅(接口隔离)' },
{ feature: '可测试性', layered: '需要集成测试', clean: '核心可单元测试' },
{ feature: '学习曲线', layered: '平缓', clean: '较陡' },
{ feature: '适用场景', layered: '中小型、快速迭代', clean: '大型复杂、长期维护' }
]
</script>
<style scoped>
.clean-architecture-demo {
padding: 24px;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%);
border-radius: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
.clean-arch-demo { padding: 20px; background: var(--vp-c-bg-soft); border-radius: 12px; }
.header { text-align: center; margin-bottom: 20px; }
.title { font-size: 16px; font-weight: 600; color: var(--vp-c-text-1); }
.subtitle { font-size: 13px; color: var(--vp-c-text-3); margin-top: 4px; }
.tabs { display: flex; gap: 8px; margin-bottom: 16px; }
.tab {
padding: 7px 16px; border: 1px solid var(--vp-c-divider); background: var(--vp-c-bg);
border-radius: 6px; cursor: pointer; font-size: 13px; color: var(--vp-c-text-2); transition: all .2s;
}
.tab:hover { color: var(--vp-c-brand-1); border-color: var(--vp-c-brand-1); }
.tab.active { background: var(--vp-c-brand-1); border-color: var(--vp-c-brand-1); color: #fff; }
.panel {
padding: 18px; border-radius: 10px;
background: var(--vp-c-bg); border: 1px solid var(--vp-c-divider);
}
.demo-header {
text-align: center;
margin-bottom: 24px;
.arch-layers, .clean-layers { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.arch-layer {
padding: 12px 14px; border-radius: 6px;
background: var(--vp-c-bg-soft); border-left: 3px solid var(--vp-c-divider);
font-size: 13px; color: var(--vp-c-text-2);
}
.arch-layer strong { color: var(--vp-c-text-1); margin-right: 8px; }
.arch-layer.green { border-left-color: #10b981; }
.arch-layer.orange { border-left-color: #f59e0b; }
.arch-layer.blue { border-left-color: #3b82f6; }
.arch-layer.teal { border-left-color: #14b8a6; }
.arch-layer.gray { border-left-color: #6b7280; }
.dep-rule {
text-align: center; padding: 10px; margin-bottom: 16px; border-radius: 6px;
border: 2px dashed var(--vp-c-brand-1); font-size: 13px; color: var(--vp-c-brand-1); font-weight: 500;
}
.demo-header h4 {
margin: 0 0 8px 0;
color: #1a1a2e;
font-size: 18px;
}
.traits { padding: 14px; border-radius: 6px; background: var(--vp-c-bg-soft); font-size: 13px; }
.traits strong { color: var(--vp-c-text-1); }
.traits ul { margin: 8px 0 0; padding-left: 18px; }
.traits li { margin: 4px 0; color: var(--vp-c-text-2); line-height: 1.5; }
.subtitle {
margin: 0;
color: #666;
font-size: 13px;
}
table { width: 100%; border-collapse: collapse; font-size: 12px; margin-bottom: 16px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid var(--vp-c-divider); color: var(--vp-c-text-2); }
th { background: var(--vp-c-bg-soft); font-weight: 600; color: var(--vp-c-text-1); }
.architecture-comparison {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.comparison-tabs {
display: flex;
background: #f5f7fa;
border-bottom: 1px solid #e4e7ed;
}
.tab-btn {
padding: 12px 24px;
border: none;
background: transparent;
color: #606266;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.tab-btn:hover {
color: #409eff;
}
.tab-btn.active {
color: #409eff;
background: white;
font-weight: 500;
}
.tab-btn.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: #409eff;
}
.comparison-content {
padding: 20px;
}
.tab-panel {
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Layered Architecture */
.arch-diagram.layered {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
margin-bottom: 20px;
}
.layer-box {
width: 100%;
max-width: 400px;
padding: 16px;
border-radius: 6px;
text-align: center;
border-left: 4px solid;
}
.layer-box.controller {
background: #f0f9ff;
border-left-color: #52c41a;
}
.layer-box.service {
background: #fff7e6;
border-left-color: #fa8c16;
}
.layer-box.repository {
background: #e6f7ff;
border-left-color: #1890ff;
}
.layer-box.domain {
background: #f6ffed;
border-left-color: #73d13d;
}
.layer-title {
font-weight: 600;
color: #1a1a2e;
margin-bottom: 4px;
}
.layer-desc {
font-size: 12px;
color: #666;
}
.arrow {
color: #909399;
font-size: 12px;
text-align: center;
}
.arch-characteristics {
padding: 16px;
background: #f8f9fa;
border-radius: 6px;
}
.arch-characteristics h5 {
margin: 0 0 12px 0;
color: #1a1a2e;
font-size: 14px;
}
.arch-characteristics ul {
margin: 0;
padding-left: 20px;
}
.arch-characteristics li {
margin: 8px 0;
color: #595959;
font-size: 13px;
line-height: 1.5;
}
/* Clean Architecture */
.arch-diagram.clean {
display: flex;
flex-direction: column;
gap: 16px;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
margin-bottom: 20px;
}
.clean-layers {
display: flex;
flex-direction: column-reverse;
gap: 8px;
}
.clean-layer {
padding: 12px 16px;
border-radius: 6px;
border-left: 4px solid;
}
.clean-layer.framework {
background: #f0f0f0;
border-left-color: #8c8c8c;
}
.clean-layer.interface {
background: #e6f7ff;
border-left-color: #1890ff;
}
.clean-layer.application {
background: #fff7e6;
border-left-color: #fa8c16;
}
.clean-layer.domain {
background: #f6ffed;
border-left-color: #52c41a;
}
.clean-layer .layer-name {
font-weight: 600;
color: #1a1a2e;
font-size: 14px;
margin-bottom: 4px;
}
.clean-layer .layer-items {
font-size: 12px;
color: #666;
}
.dependency-rule {
background: white;
border-radius: 6px;
padding: 16px;
text-align: center;
border: 2px dashed #1890ff;
}
.rule-arrow {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 8px;
}
.rule-arrow .arrow-line {
width: 2px;
height: 20px;
background: #1890ff;
}
.rule-arrow .arrow-head {
color: #1890ff;
font-weight: 600;
font-size: 14px;
}
.rule-text {
color: #595959;
font-size: 13px;
}
/* Comparison Table */
.comparison-table {
overflow-x: auto;
}
.comparison-table table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
.comparison-table th,
.comparison-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e8e8e8;
}
.comparison-table th {
background: #f5f7fa;
font-weight: 600;
color: #1a1a2e;
}
.comparison-table tr:hover {
background: #fafafa;
}
/* Recommendation */
.recommendation {
margin-top: 24px;
}
.recommendation h5 {
margin: 0 0 16px 0;
color: #1a1a2e;
font-size: 15px;
text-align: center;
}
.rec-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.rec-card {
background: #f8f9fa;
border-radius: 6px;
padding: 16px;
position: relative;
}
.rec-card.recommended {
background: #f6ffed;
border: 2px solid #52c41a;
}
.rec-badge {
position: absolute;
top: -10px;
right: 16px;
background: #52c41a;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 11px;
font-weight: 600;
}
.rec-title {
font-weight: 600;
color: #1a1a2e;
margin-bottom: 12px;
font-size: 14px;
}
.rec-card ul {
margin: 0;
padding-left: 18px;
}
.rec-card li {
margin: 6px 0;
color: #595959;
font-size: 12px;
line-height: 1.5;
}
.rec-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.rec-card { padding: 14px; border-radius: 6px; background: var(--vp-c-bg-soft); font-size: 12px; }
.rec-card strong { font-size: 13px; color: var(--vp-c-text-1); display: block; margin-bottom: 8px; }
.rec-card ul { margin: 0; padding-left: 16px; }
.rec-card li { margin: 4px 0; color: var(--vp-c-text-2); }
.rec-card.recommended { border: 2px solid var(--vp-c-green-1); background: var(--vp-c-green-soft); }
@media (max-width: 768px) {
.rec-grid {
grid-template-columns: 1fr;
}
.rec-grid { grid-template-columns: 1fr; }
}
</style>