timsquad 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +347 -0
- package/bin/tsq.js +6 -0
- package/dist/commands/feedback.d.ts +3 -0
- package/dist/commands/feedback.d.ts.map +1 -0
- package/dist/commands/feedback.js +142 -0
- package/dist/commands/feedback.js.map +1 -0
- package/dist/commands/full.d.ts +3 -0
- package/dist/commands/full.d.ts.map +1 -0
- package/dist/commands/full.js +87 -0
- package/dist/commands/full.js.map +1 -0
- package/dist/commands/git/commit.d.ts +3 -0
- package/dist/commands/git/commit.d.ts.map +1 -0
- package/dist/commands/git/commit.js +88 -0
- package/dist/commands/git/commit.js.map +1 -0
- package/dist/commands/git/index.d.ts +5 -0
- package/dist/commands/git/index.d.ts.map +1 -0
- package/dist/commands/git/index.js +5 -0
- package/dist/commands/git/index.js.map +1 -0
- package/dist/commands/git/pr.d.ts +3 -0
- package/dist/commands/git/pr.d.ts.map +1 -0
- package/dist/commands/git/pr.js +138 -0
- package/dist/commands/git/pr.js.map +1 -0
- package/dist/commands/git/release.d.ts +3 -0
- package/dist/commands/git/release.d.ts.map +1 -0
- package/dist/commands/git/release.js +158 -0
- package/dist/commands/git/release.js.map +1 -0
- package/dist/commands/git/sync.d.ts +3 -0
- package/dist/commands/git/sync.d.ts.map +1 -0
- package/dist/commands/git/sync.js +132 -0
- package/dist/commands/git/sync.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +150 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/log.d.ts +3 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +271 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/metrics.d.ts +3 -0
- package/dist/commands/metrics.d.ts.map +1 -0
- package/dist/commands/metrics.js +299 -0
- package/dist/commands/metrics.js.map +1 -0
- package/dist/commands/quick.d.ts +3 -0
- package/dist/commands/quick.d.ts.map +1 -0
- package/dist/commands/quick.js +136 -0
- package/dist/commands/quick.js.map +1 -0
- package/dist/commands/retro.d.ts +3 -0
- package/dist/commands/retro.d.ts.map +1 -0
- package/dist/commands/retro.js +280 -0
- package/dist/commands/retro.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +127 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/watch.d.ts +3 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +213 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +34 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +108 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/project.d.ts +47 -0
- package/dist/lib/project.d.ts.map +1 -0
- package/dist/lib/project.js +191 -0
- package/dist/lib/project.js.map +1 -0
- package/dist/lib/template.d.ts +33 -0
- package/dist/lib/template.d.ts.map +1 -0
- package/dist/lib/template.js +151 -0
- package/dist/lib/template.js.map +1 -0
- package/dist/types/config.d.ts +75 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +66 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/feedback.d.ts +59 -0
- package/dist/types/feedback.d.ts.map +1 -0
- package/dist/types/feedback.js +26 -0
- package/dist/types/feedback.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/project.d.ts +89 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +44 -0
- package/dist/types/project.js.map +1 -0
- package/dist/utils/colors.d.ts +30 -0
- package/dist/utils/colors.d.ts.map +1 -0
- package/dist/utils/colors.js +54 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/date.d.ts +25 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +65 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/fs.d.ts +49 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +84 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/prompts.d.ts +31 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +95 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/yaml.d.ts +21 -0
- package/dist/utils/yaml.d.ts.map +1 -0
- package/dist/utils/yaml.js +40 -0
- package/dist/utils/yaml.js.map +1 -0
- package/package.json +71 -0
- package/templates/common/CLAUDE.md.template +254 -0
- package/templates/common/claude/agents/tsq-dba.md +290 -0
- package/templates/common/claude/agents/tsq-designer.md +304 -0
- package/templates/common/claude/agents/tsq-developer.md +118 -0
- package/templates/common/claude/agents/tsq-planner.md +90 -0
- package/templates/common/claude/agents/tsq-prompter.md +336 -0
- package/templates/common/claude/agents/tsq-qa.md +134 -0
- package/templates/common/claude/agents/tsq-retro.md +168 -0
- package/templates/common/claude/agents/tsq-security.md +190 -0
- package/templates/common/claude/skills/architecture/SKILL.md +123 -0
- package/templates/common/claude/skills/backend/node/SKILL.md +1015 -0
- package/templates/common/claude/skills/coding/SKILL.md +171 -0
- package/templates/common/claude/skills/database/prisma/SKILL.md +357 -0
- package/templates/common/claude/skills/frontend/nextjs/SKILL.md +279 -0
- package/templates/common/claude/skills/frontend/react/SKILL.md +1729 -0
- package/templates/common/claude/skills/methodology/bdd/SKILL.md +234 -0
- package/templates/common/claude/skills/methodology/ddd/SKILL.md +311 -0
- package/templates/common/claude/skills/methodology/tdd/SKILL.md +512 -0
- package/templates/common/claude/skills/planning/SKILL.md +90 -0
- package/templates/common/claude/skills/security/SKILL.md +234 -0
- package/templates/common/claude/skills/testing/SKILL.md +146 -0
- package/templates/common/claude/skills/typescript/SKILL.md +435 -0
- package/templates/common/config.template.yaml +131 -0
- package/templates/common/timsquad/architectures/clean/ARCHITECTURE.md +49 -0
- package/templates/common/timsquad/architectures/clean/backend.xml +210 -0
- package/templates/common/timsquad/architectures/clean/frontend.xml +148 -0
- package/templates/common/timsquad/architectures/fsd/ARCHITECTURE.md +67 -0
- package/templates/common/timsquad/architectures/fsd/frontend.xml +288 -0
- package/templates/common/timsquad/architectures/hexagonal/ARCHITECTURE.md +60 -0
- package/templates/common/timsquad/architectures/hexagonal/backend.xml +300 -0
- package/templates/common/timsquad/constraints/competency-framework.xml +501 -0
- package/templates/common/timsquad/constraints/ssot-schema.xml +433 -0
- package/templates/common/timsquad/feedback/feedback-router.sh +341 -0
- package/templates/common/timsquad/feedback/routing-rules.yaml +352 -0
- package/templates/common/timsquad/generators/data-design.xml +290 -0
- package/templates/common/timsquad/generators/prd.xml +280 -0
- package/templates/common/timsquad/generators/requirements.xml +220 -0
- package/templates/common/timsquad/generators/service-spec.xml +266 -0
- package/templates/common/timsquad/logs/_example.md +81 -0
- package/templates/common/timsquad/logs/_template.md +46 -0
- package/templates/common/timsquad/patterns/cqrs.xml +127 -0
- package/templates/common/timsquad/patterns/event-sourcing.xml +85 -0
- package/templates/common/timsquad/patterns/repository.xml +64 -0
- package/templates/common/timsquad/process/state-machine.xml +343 -0
- package/templates/common/timsquad/process/validation-rules.xml +308 -0
- package/templates/common/timsquad/process/workflow-base.xml +202 -0
- package/templates/common/timsquad/retrospective/cycle-report.template.md +205 -0
- package/templates/common/timsquad/retrospective/metrics/metrics-schema.json +203 -0
- package/templates/common/timsquad/retrospective/patterns/failure-patterns.md +199 -0
- package/templates/common/timsquad/retrospective/patterns/success-patterns.md +262 -0
- package/templates/common/timsquad/retrospective/retrospective-config.xml +294 -0
- package/templates/common/timsquad/retrospective/retrospective-state.xml +210 -0
- package/templates/common/timsquad/ssot/adr/ADR-000-template.md +121 -0
- package/templates/common/timsquad/ssot/adr/ADR-001-example.md +115 -0
- package/templates/common/timsquad/ssot/data-design.template.md +132 -0
- package/templates/common/timsquad/ssot/deployment-spec.template.md +384 -0
- package/templates/common/timsquad/ssot/env-config.template.md +346 -0
- package/templates/common/timsquad/ssot/error-codes.template.md +114 -0
- package/templates/common/timsquad/ssot/functional-spec.template.md +185 -0
- package/templates/common/timsquad/ssot/glossary.template.md +148 -0
- package/templates/common/timsquad/ssot/integration-spec.template.md +391 -0
- package/templates/common/timsquad/ssot/planning.template.md +94 -0
- package/templates/common/timsquad/ssot/prd.template.md +102 -0
- package/templates/common/timsquad/ssot/requirements.template.md +117 -0
- package/templates/common/timsquad/ssot/security-spec.template.md +309 -0
- package/templates/common/timsquad/ssot/service-spec.template.md +194 -0
- package/templates/common/timsquad/ssot/test-spec.template.md +264 -0
- package/templates/common/timsquad/ssot/ui-ux-spec.template.md +262 -0
- package/templates/common/timsquad/state/workspace.xml +217 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bdd
|
|
3
|
+
description: Behavior-Driven Development κ°μ΄λλΌμΈ
|
|
4
|
+
user-invocable: false
|
|
5
|
+
compatible-with: [tdd, ddd]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<skill name="bdd">
|
|
9
|
+
<purpose>λΉμ¦λμ€ νμ μ€μ¬μ ν
μ€νΈ λ° κ°λ° λ°©λ²λ‘ </purpose>
|
|
10
|
+
|
|
11
|
+
<philosophy>
|
|
12
|
+
<principle>λΉμ¦λμ€ μΈμ΄λ‘ μꡬμ¬ν νν</principle>
|
|
13
|
+
<principle>Given-When-ThenμΌλ‘ μλλ¦¬μ€ λͺ
μΈ</principle>
|
|
14
|
+
<principle>μ΄μμλ λ¬Έμ (Living Documentation)</principle>
|
|
15
|
+
<principle>κ°λ°μ, QA, κΈ°νμ κ° κ³΅ν΅ μΈμ΄</principle>
|
|
16
|
+
</philosophy>
|
|
17
|
+
|
|
18
|
+
<core-concepts>
|
|
19
|
+
<concept name="user-story">
|
|
20
|
+
<description>μ¬μ©μ κ΄μ μ κΈ°λ₯ μ€λͺ
</description>
|
|
21
|
+
<template>
|
|
22
|
+
As a [μν ]
|
|
23
|
+
I want [κΈ°λ₯]
|
|
24
|
+
So that [κ°μΉ/μ΄μ ]
|
|
25
|
+
</template>
|
|
26
|
+
<example>
|
|
27
|
+
As a νμ
|
|
28
|
+
I want λΉλ°λ²νΈλ₯Ό μ¬μ€μ
|
|
29
|
+
So that κ³μ μ λ€μ μ κ·Όν μ μλ€
|
|
30
|
+
</example>
|
|
31
|
+
</concept>
|
|
32
|
+
|
|
33
|
+
<concept name="scenario">
|
|
34
|
+
<description>ꡬ체μ μΈ νμ μλ리μ€</description>
|
|
35
|
+
<template>
|
|
36
|
+
Given [μ¬μ 쑰건]
|
|
37
|
+
When [νμ]
|
|
38
|
+
Then [κΈ°λ κ²°κ³Ό]
|
|
39
|
+
</template>
|
|
40
|
+
</concept>
|
|
41
|
+
|
|
42
|
+
<concept name="acceptance-criteria">
|
|
43
|
+
<description>μΈμ 쑰건 - κΈ°λ₯ μλ£ κΈ°μ€</description>
|
|
44
|
+
</concept>
|
|
45
|
+
</core-concepts>
|
|
46
|
+
|
|
47
|
+
<gherkin-syntax>
|
|
48
|
+
<description>BDD μλλ¦¬μ€ μμ± λ¬Έλ²</description>
|
|
49
|
+
|
|
50
|
+
<example type="feature-file">
|
|
51
|
+
<title>λ‘κ·ΈμΈ κΈ°λ₯</title>
|
|
52
|
+
<code><![CDATA[
|
|
53
|
+
Feature: μ¬μ©μ λ‘κ·ΈμΈ
|
|
54
|
+
μ¬μ©μκ° μ΄λ©μΌκ³Ό λΉλ°λ²νΈλ‘ λ‘κ·ΈμΈν μ μλ€
|
|
55
|
+
|
|
56
|
+
Background:
|
|
57
|
+
Given νμκ°μ
λ μ¬μ©μκ° μ‘΄μ¬νλ€
|
|
58
|
+
| email | password |
|
|
59
|
+
| user@test.com | password1 |
|
|
60
|
+
|
|
61
|
+
Scenario: μ¬λ°λ₯Έ μ격 μ¦λͺ
μΌλ‘ λ‘κ·ΈμΈ μ±κ³΅
|
|
62
|
+
Given λ‘κ·ΈμΈ νμ΄μ§μ μ μνλ€
|
|
63
|
+
When μ΄λ©μΌ "user@test.com"μ μ
λ ₯νλ€
|
|
64
|
+
And λΉλ°λ²νΈ "password1"μ μ
λ ₯νλ€
|
|
65
|
+
And λ‘κ·ΈμΈ λ²νΌμ ν΄λ¦νλ€
|
|
66
|
+
Then λμ보λ νμ΄μ§λ‘ μ΄λνλ€
|
|
67
|
+
And "λ‘κ·ΈμΈ μ±κ³΅" λ©μμ§κ° νμλλ€
|
|
68
|
+
|
|
69
|
+
Scenario: μλͺ»λ λΉλ°λ²νΈλ‘ λ‘κ·ΈμΈ μ€ν¨
|
|
70
|
+
Given λ‘κ·ΈμΈ νμ΄μ§μ μ μνλ€
|
|
71
|
+
When μ΄λ©μΌ "user@test.com"μ μ
λ ₯νλ€
|
|
72
|
+
And λΉλ°λ²νΈ "wrongpassword"λ₯Ό μ
λ ₯νλ€
|
|
73
|
+
And λ‘κ·ΈμΈ λ²νΌμ ν΄λ¦νλ€
|
|
74
|
+
Then λ‘κ·ΈμΈ νμ΄μ§μ 머무λ₯Έλ€
|
|
75
|
+
And "μ΄λ©μΌ λλ λΉλ°λ²νΈκ° μ¬λ°λ₯΄μ§ μμ΅λλ€" μλ¬κ° νμλλ€
|
|
76
|
+
|
|
77
|
+
Scenario Outline: λ€μν μλͺ»λ μ
λ ₯
|
|
78
|
+
When μ΄λ©μΌ "<email>"μ μ
λ ₯νλ€
|
|
79
|
+
And λΉλ°λ²νΈ "<password>"λ₯Ό μ
λ ₯νλ€
|
|
80
|
+
And λ‘κ·ΈμΈ λ²νΌμ ν΄λ¦νλ€
|
|
81
|
+
Then "<error>" μλ¬κ° νμλλ€
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
| email | password | error |
|
|
85
|
+
| | pass | μ΄λ©μΌμ μ
λ ₯ν΄μ£ΌμΈμ |
|
|
86
|
+
| invalid-email | pass | μ¬λ°λ₯Έ μ΄λ©μΌ νμμ΄ μλλλ€ |
|
|
87
|
+
| user@test.com | | λΉλ°λ²νΈλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ |
|
|
88
|
+
]]></code>
|
|
89
|
+
</example>
|
|
90
|
+
</gherkin-syntax>
|
|
91
|
+
|
|
92
|
+
<implementation-patterns>
|
|
93
|
+
<pattern name="step-definitions" language="typescript">
|
|
94
|
+
<description>Gherkin μ€ν
μ μ½λλ‘ μ°κ²°</description>
|
|
95
|
+
<example>
|
|
96
|
+
<title>Playwright + Cucumber μμ</title>
|
|
97
|
+
<code><![CDATA[
|
|
98
|
+
import { Given, When, Then } from '@cucumber/cucumber';
|
|
99
|
+
import { expect } from '@playwright/test';
|
|
100
|
+
|
|
101
|
+
Given('λ‘κ·ΈμΈ νμ΄μ§μ μ μνλ€', async function() {
|
|
102
|
+
await this.page.goto('/login');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
When('μ΄λ©μΌ {string}μ μ
λ ₯νλ€', async function(email: string) {
|
|
106
|
+
await this.page.fill('[data-testid="email-input"]', email);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
When('λΉλ°λ²νΈ {string}λ₯Ό μ
λ ₯νλ€', async function(password: string) {
|
|
110
|
+
await this.page.fill('[data-testid="password-input"]', password);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
When('λ‘κ·ΈμΈ λ²νΌμ ν΄λ¦νλ€', async function() {
|
|
114
|
+
await this.page.click('[data-testid="login-button"]');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
Then('λμ보λ νμ΄μ§λ‘ μ΄λνλ€', async function() {
|
|
118
|
+
await expect(this.page).toHaveURL('/dashboard');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
Then('{string} λ©μμ§κ° νμλλ€', async function(message: string) {
|
|
122
|
+
await expect(this.page.locator('[data-testid="toast"]'))
|
|
123
|
+
.toContainText(message);
|
|
124
|
+
});
|
|
125
|
+
]]></code>
|
|
126
|
+
</example>
|
|
127
|
+
</pattern>
|
|
128
|
+
|
|
129
|
+
<pattern name="api-bdd" language="typescript">
|
|
130
|
+
<description>API ν
μ€νΈμ© BDD</description>
|
|
131
|
+
<example>
|
|
132
|
+
<title>Vitest + SuperTest μμ</title>
|
|
133
|
+
<code><![CDATA[
|
|
134
|
+
describe('Feature: μ¬μ©μ λ±λ‘ API', () => {
|
|
135
|
+
describe('Scenario: μ ν¨ν μ λ³΄λ‘ νμκ°μ
', () => {
|
|
136
|
+
it('Given μ ν¨ν μ¬μ©μ μ λ³΄κ° μλ€', () => {
|
|
137
|
+
ctx.userData = {
|
|
138
|
+
email: 'new@test.com',
|
|
139
|
+
password: 'SecurePass123!',
|
|
140
|
+
name: 'νκΈΈλ'
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('When POST /api/users μμ²μ 보λΈλ€', async () => {
|
|
145
|
+
ctx.response = await request(app)
|
|
146
|
+
.post('/api/users')
|
|
147
|
+
.send(ctx.userData);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('Then 201 μν μ½λλ₯Ό λ°ννλ€', () => {
|
|
151
|
+
expect(ctx.response.status).toBe(201);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('And μμ±λ μ¬μ©μ IDλ₯Ό λ°ννλ€', () => {
|
|
155
|
+
expect(ctx.response.body.data.id).toBeDefined();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
]]></code>
|
|
160
|
+
</example>
|
|
161
|
+
</pattern>
|
|
162
|
+
</implementation-patterns>
|
|
163
|
+
|
|
164
|
+
<bdd-vs-tdd>
|
|
165
|
+
<comparison>
|
|
166
|
+
<aspect name="κ΄μ ">
|
|
167
|
+
<tdd>κ°λ°μ κ΄μ (μ½λ λ¨μ)</tdd>
|
|
168
|
+
<bdd>μ¬μ©μ/λΉμ¦λμ€ κ΄μ (νμ λ¨μ)</bdd>
|
|
169
|
+
</aspect>
|
|
170
|
+
<aspect name="μΈμ΄">
|
|
171
|
+
<tdd>κΈ°μ μ©μ΄ (ν¨μ, ν΄λμ€)</tdd>
|
|
172
|
+
<bdd>λΉμ¦λμ€ μ©μ΄ (μλ리μ€, κΈ°λ₯)</bdd>
|
|
173
|
+
</aspect>
|
|
174
|
+
<aspect name="ν
μ€νΈ λμ">
|
|
175
|
+
<tdd>λ¨μ ν
μ€νΈ μ€μ¬</tdd>
|
|
176
|
+
<bdd>μΈμ ν
μ€νΈ, E2E μ€μ¬</bdd>
|
|
177
|
+
</aspect>
|
|
178
|
+
<aspect name="λ¬Έμν">
|
|
179
|
+
<tdd>μ½λκ° λ¬Έμ</tdd>
|
|
180
|
+
<bdd>μλ리μ€κ° μ΄μμλ λ¬Έμ</bdd>
|
|
181
|
+
</aspect>
|
|
182
|
+
</comparison>
|
|
183
|
+
<recommendation>TDD + BDD ν¨κ» μ¬μ© κΆμ₯ (보μ κ΄κ³)</recommendation>
|
|
184
|
+
</bdd-vs-tdd>
|
|
185
|
+
|
|
186
|
+
<directory-structure>
|
|
187
|
+
<dir name="features">
|
|
188
|
+
<description>Gherkin feature νμΌ</description>
|
|
189
|
+
<file name="auth/login.feature" />
|
|
190
|
+
<file name="auth/register.feature" />
|
|
191
|
+
<file name="order/checkout.feature" />
|
|
192
|
+
</dir>
|
|
193
|
+
<dir name="step-definitions">
|
|
194
|
+
<description>μ€ν
ꡬν</description>
|
|
195
|
+
<file name="auth.steps.ts" />
|
|
196
|
+
<file name="order.steps.ts" />
|
|
197
|
+
<file name="common.steps.ts" />
|
|
198
|
+
</dir>
|
|
199
|
+
<dir name="support">
|
|
200
|
+
<description>ν¬νΌ, ν
</description>
|
|
201
|
+
<file name="world.ts" example="ν
μ€νΈ 컨ν
μ€νΈ" />
|
|
202
|
+
<file name="hooks.ts" example="Before, After ν
" />
|
|
203
|
+
</dir>
|
|
204
|
+
</directory-structure>
|
|
205
|
+
|
|
206
|
+
<tools>
|
|
207
|
+
<tool name="cucumber-js">JavaScript/TypeScript Cucumber ꡬν</tool>
|
|
208
|
+
<tool name="playwright">E2E ν
μ€νΈ λ¬λ</tool>
|
|
209
|
+
<tool name="jest-cucumber">Jestμ Cucumber ν΅ν©</tool>
|
|
210
|
+
</tools>
|
|
211
|
+
|
|
212
|
+
<rules>
|
|
213
|
+
<category name="νμ">
|
|
214
|
+
<must>μλ리μ€λ λΉμ¦λμ€ μΈμ΄λ‘ μμ±</must>
|
|
215
|
+
<must>Given-When-Then νμ μ€μ</must>
|
|
216
|
+
<must>νλμ μλ리μ€λ νλμ νμλ§ ν
μ€νΈ</must>
|
|
217
|
+
<must>μλ리μ€λ λ
립μ μΌλ‘ μ€ν κ°λ₯ν΄μΌ ν¨</must>
|
|
218
|
+
</category>
|
|
219
|
+
<category name="κΈμ§">
|
|
220
|
+
<must-not>κΈ°μ μ©μ΄λ₯Ό μλ리μ€μ λ
ΈμΆ (CSS selector, API endpoint λ±)</must-not>
|
|
221
|
+
<must-not>ν
μ€νΈ κ° μν 곡μ </must-not>
|
|
222
|
+
<must-not>UI ꡬν μΈλΆμ¬ν μλ리μ€μ ν¬ν¨</must-not>
|
|
223
|
+
</category>
|
|
224
|
+
</rules>
|
|
225
|
+
|
|
226
|
+
<checklist>
|
|
227
|
+
<item priority="critical">Feature νμΌμ΄ λΉμ¦λμ€ μΈμ΄λ‘ μμ±λ¨</item>
|
|
228
|
+
<item priority="critical">Given-When-Then νμ μ€μ</item>
|
|
229
|
+
<item priority="high">μλ리μ€κ° λ
립μ μΌλ‘ μ€ν κ°λ₯</item>
|
|
230
|
+
<item priority="high">Step Definition μ¬μ¬μ©μ± ν보</item>
|
|
231
|
+
<item priority="medium">Backgroundλ‘ κ³΅ν΅ μ μ 쑰건 μΆμΆ</item>
|
|
232
|
+
<item priority="medium">Scenario OutlineμΌλ‘ λ°μ΄ν° μ£Όλ ν
μ€νΈ</item>
|
|
233
|
+
</checklist>
|
|
234
|
+
</skill>
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ddd
|
|
3
|
+
description: Domain-Driven Design μ λ΅μ μ€κ³ κ°μ΄λλΌμΈ
|
|
4
|
+
user-invocable: false
|
|
5
|
+
compatible-with: [tdd, bdd]
|
|
6
|
+
note: μ μ μ ν¨ν΄(Aggregate, Entity λ±)μ architectures/μ ν¬ν¨λ¨
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<skill name="ddd">
|
|
10
|
+
<purpose>볡μ‘ν λλ©μΈμ ν¨κ³Όμ μΌλ‘ λͺ¨λΈλ§νλ μ λ΅μ μ€κ³ λ°©λ²λ‘ </purpose>
|
|
11
|
+
|
|
12
|
+
<philosophy>
|
|
13
|
+
<principle>λλ©μΈ μ λ¬Έκ°μ κ°λ°μμ νμ
</principle>
|
|
14
|
+
<principle>μ λΉμΏΌν°μ€ μΈμ΄ (Ubiquitous Language)</principle>
|
|
15
|
+
<principle>λͺ¨λΈ μ£Όλ μ€κ³ (Model-Driven Design)</principle>
|
|
16
|
+
<principle>κ²½κ³ μ»¨ν
μ€νΈλ‘ 볡μ‘μ± λΆλ¦¬</principle>
|
|
17
|
+
</philosophy>
|
|
18
|
+
|
|
19
|
+
<strategic-vs-tactical>
|
|
20
|
+
<strategic location="μ΄ μ€ν¬">
|
|
21
|
+
<item>Bounded Context (κ²½κ³ μ»¨ν
μ€νΈ)</item>
|
|
22
|
+
<item>Ubiquitous Language (μ λΉμΏΌν°μ€ μΈμ΄)</item>
|
|
23
|
+
<item>Context Map (컨ν
μ€νΈ λ§΅)</item>
|
|
24
|
+
<item>Subdomain (μλΈλλ©μΈ)</item>
|
|
25
|
+
</strategic>
|
|
26
|
+
<tactical location="architectures/ λ° patterns/">
|
|
27
|
+
<item>Aggregate, Entity, Value Object β clean/backend.xml</item>
|
|
28
|
+
<item>Repository β patterns/repository.xml</item>
|
|
29
|
+
<item>Domain Event β patterns/event-sourcing.xml</item>
|
|
30
|
+
</tactical>
|
|
31
|
+
</strategic-vs-tactical>
|
|
32
|
+
|
|
33
|
+
<core-concepts>
|
|
34
|
+
<concept name="ubiquitous-language">
|
|
35
|
+
<description>
|
|
36
|
+
λλ©μΈ μ λ¬Έκ°μ κ°λ°μκ° κ³΅μ νλ κ³΅ν΅ μΈμ΄.
|
|
37
|
+
μ½λ, λ¬Έμ, λν λͺ¨λμμ λμΌν μ©μ΄ μ¬μ©.
|
|
38
|
+
</description>
|
|
39
|
+
<example type="bad">
|
|
40
|
+
<code><![CDATA[
|
|
41
|
+
// κ°λ°μ μ©μ΄λ‘ μμ± - λλ©μΈ μ λ¬Έκ°κ° μ΄ν΄ λͺ»ν¨
|
|
42
|
+
class DataProcessor {
|
|
43
|
+
processRecord(record: Record) {
|
|
44
|
+
if (record.status === 1) {
|
|
45
|
+
this.updateFlag(record, true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]]></code>
|
|
50
|
+
</example>
|
|
51
|
+
<example type="good">
|
|
52
|
+
<code><![CDATA[
|
|
53
|
+
// μ λΉμΏΌν°μ€ μΈμ΄ μ¬μ© - λλ©μΈ μ λ¬Έκ°λ μ΄ν΄ κ°λ₯
|
|
54
|
+
class OrderFulfillment {
|
|
55
|
+
shipOrder(order: Order) {
|
|
56
|
+
if (order.isPaid()) {
|
|
57
|
+
order.markAsShipped();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]]></code>
|
|
62
|
+
</example>
|
|
63
|
+
<practice>
|
|
64
|
+
<item>μ©μ΄ μ¬μ (Glossary) μμ± λ° μ μ§</item>
|
|
65
|
+
<item>μ½λ 리뷰 μ λλ©μΈ μ©μ΄ μ€μ νμΈ</item>
|
|
66
|
+
<item>λλ©μΈ μ λ¬Έκ°μ μ κΈ°μ μ©μ΄ κ²ν </item>
|
|
67
|
+
</practice>
|
|
68
|
+
</concept>
|
|
69
|
+
|
|
70
|
+
<concept name="bounded-context">
|
|
71
|
+
<description>
|
|
72
|
+
νΉμ λλ©μΈ λͺ¨λΈμ΄ μ μ©λλ λͺ
μμ κ²½κ³.
|
|
73
|
+
κ°μ μ©μ΄λ 컨ν
μ€νΈλ§λ€ λ€λ₯Έ μλ―Έλ₯Ό κ°μ§ μ μμ.
|
|
74
|
+
</description>
|
|
75
|
+
<example>
|
|
76
|
+
<title>"Product"μ λ€λ₯Έ μλ―Έ</title>
|
|
77
|
+
<context name="Catalog">μν μ 보, μ€λͺ
, μ΄λ―Έμ§</context>
|
|
78
|
+
<context name="Inventory">μ¬κ³ μλ, μμΉ, SKU</context>
|
|
79
|
+
<context name="Pricing">κ°κ²©, ν μΈ, νλ‘λͺ¨μ
</context>
|
|
80
|
+
<context name="Shipping">무κ², ν¬κΈ°, λ°°μ‘ μ ν</context>
|
|
81
|
+
</example>
|
|
82
|
+
<guidelines>
|
|
83
|
+
<guideline>νλμ Bounded Context = νλμ ν</guideline>
|
|
84
|
+
<guideline>νλμ Bounded Context = νλμ μ½λλ² μ΄μ€ (λλ λͺ¨λ)</guideline>
|
|
85
|
+
<guideline>컨ν
μ€νΈ κ° ν΅μ μ λͺ
μμ μΈν°νμ΄μ€λ‘</guideline>
|
|
86
|
+
</guidelines>
|
|
87
|
+
</concept>
|
|
88
|
+
|
|
89
|
+
<concept name="subdomain">
|
|
90
|
+
<description>λΉμ¦λμ€ λλ©μΈμ λ
Όλ¦¬μ λΆν </description>
|
|
91
|
+
<types>
|
|
92
|
+
<type name="core">
|
|
93
|
+
<description>λΉμ¦λμ€ ν΅μ¬ μ°¨λ³ν μμ</description>
|
|
94
|
+
<strategy>μ΅κ³ μΈλ ₯ ν¬μ
, μ체 κ°λ°</strategy>
|
|
95
|
+
<example>μΏ ν‘μ λ‘μΌλ°°μ‘ μμ€ν
</example>
|
|
96
|
+
</type>
|
|
97
|
+
<type name="supporting">
|
|
98
|
+
<description>Coreλ₯Ό μ§μνμ§λ§ μ°¨λ³ν μμ μλ</description>
|
|
99
|
+
<strategy>μμμμ± λλ μ λΉν νμ§</strategy>
|
|
100
|
+
<example>λ΄λΆ κ΄λ¦¬ μμ€ν
</example>
|
|
101
|
+
</type>
|
|
102
|
+
<type name="generic">
|
|
103
|
+
<description>λͺ¨λ λΉμ¦λμ€μ 곡ν΅</description>
|
|
104
|
+
<strategy>ꡬ맀 λλ μ€νμμ€ μ¬μ©</strategy>
|
|
105
|
+
<example>κ²°μ , μΈμ¦, μ΄λ©μΌ λ°μ‘</example>
|
|
106
|
+
</type>
|
|
107
|
+
</types>
|
|
108
|
+
</concept>
|
|
109
|
+
|
|
110
|
+
<concept name="context-map">
|
|
111
|
+
<description>Bounded Context κ°μ κ΄κ³λ₯Ό μκ°ν</description>
|
|
112
|
+
<relationships>
|
|
113
|
+
<relationship name="partnership">
|
|
114
|
+
<description>λ νμ΄ ν¨κ» μ±κ³΅/μ€ν¨. κΈ΄λ°ν νλ ₯</description>
|
|
115
|
+
<diagram>[Context A] βPartnershipβ [Context B]</diagram>
|
|
116
|
+
</relationship>
|
|
117
|
+
<relationship name="shared-kernel">
|
|
118
|
+
<description>곡μ λͺ¨λΈ. μμͺ½ ν©μ νμ</description>
|
|
119
|
+
<diagram>[Context A] βShared Kernelβ [Context B]</diagram>
|
|
120
|
+
<warning>λ³κ²½ μ μμͺ½ μν₯. μ΅μν κΆμ₯</warning>
|
|
121
|
+
</relationship>
|
|
122
|
+
<relationship name="customer-supplier">
|
|
123
|
+
<description>Upstream(곡κΈμ)μ΄ Downstream(κ³ κ°) μꡬ μμ©</description>
|
|
124
|
+
<diagram>[Supplier] βCustomer-Supplierβ [Customer]</diagram>
|
|
125
|
+
</relationship>
|
|
126
|
+
<relationship name="conformist">
|
|
127
|
+
<description>Downstreamμ΄ Upstream λͺ¨λΈμ κ·Έλλ‘ μμ©</description>
|
|
128
|
+
<diagram>[Upstream] βConformistβ [Downstream]</diagram>
|
|
129
|
+
<usecase>μΈλΆ API μ°λ μ</usecase>
|
|
130
|
+
</relationship>
|
|
131
|
+
<relationship name="anticorruption-layer">
|
|
132
|
+
<description>λ²μ λ μ΄μ΄λ‘ μΈλΆ λͺ¨λΈλ‘λΆν° 보νΈ</description>
|
|
133
|
+
<diagram>[External] βACLβ [Our Context]</diagram>
|
|
134
|
+
<usecase>λ κ±°μ μμ€ν
μ°λ, μΈλΆ API</usecase>
|
|
135
|
+
</relationship>
|
|
136
|
+
<relationship name="open-host-service">
|
|
137
|
+
<description>μ μ μλ νλ‘ν μ½λ‘ μλΉμ€ μ 곡</description>
|
|
138
|
+
<diagram>[Context] βOHS/PLβ [Multiple Consumers]</diagram>
|
|
139
|
+
</relationship>
|
|
140
|
+
<relationship name="published-language">
|
|
141
|
+
<description>νμ€νλ κ΅ν μΈμ΄ (JSON Schema, Protobuf)</description>
|
|
142
|
+
</relationship>
|
|
143
|
+
</relationships>
|
|
144
|
+
</concept>
|
|
145
|
+
</core-concepts>
|
|
146
|
+
|
|
147
|
+
<context-map-example>
|
|
148
|
+
<title>μ΄μ»€λ¨Έμ€ Context Map</title>
|
|
149
|
+
<diagram><![CDATA[
|
|
150
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
151
|
+
β E-Commerce System β
|
|
152
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
|
|
153
|
+
β β
|
|
154
|
+
β ββββββββββββββββ Partnership ββββββββββββββββ β
|
|
155
|
+
β β Catalog ββββββββββββββββββββββββΊβ Pricing β β
|
|
156
|
+
β β Context β β Context β β
|
|
157
|
+
β ββββββββ¬ββββββββ ββββββββββββββββ β
|
|
158
|
+
β β β
|
|
159
|
+
β β Customer-Supplier β
|
|
160
|
+
β βΌ β
|
|
161
|
+
β ββββββββββββββββ ββββββββββββββββ β
|
|
162
|
+
β β Order β Shared Kernel β Inventory β β
|
|
163
|
+
β β Context ββββββββββββββββββββββββΊβ Context β β
|
|
164
|
+
β ββββββββ¬ββββββββ (Product ID) ββββββββββββββββ β
|
|
165
|
+
β β β
|
|
166
|
+
β β ACL β
|
|
167
|
+
β βΌ β
|
|
168
|
+
β ββββββββββββββββ ββββββββββββββββ β
|
|
169
|
+
β β Shipping β β Payment β β
|
|
170
|
+
β β Context β β Context β β
|
|
171
|
+
β ββββββββββββββββ ββββββββ¬ββββββββ β
|
|
172
|
+
β β ACL β
|
|
173
|
+
β βΌ β
|
|
174
|
+
β ββββββββββββββββ β
|
|
175
|
+
β β Stripe β β
|
|
176
|
+
β β (External) β β
|
|
177
|
+
β ββββββββββββββββ β
|
|
178
|
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
179
|
+
]]></diagram>
|
|
180
|
+
</context-map-example>
|
|
181
|
+
|
|
182
|
+
<anticorruption-layer-pattern>
|
|
183
|
+
<description>μΈλΆ λͺ¨λΈλ‘λΆν° λλ©μΈ 보νΈ</description>
|
|
184
|
+
<example language="typescript">
|
|
185
|
+
<title>Stripe κ²°μ μ°λ ACL</title>
|
|
186
|
+
<code><![CDATA[
|
|
187
|
+
// μΈλΆ λͺ¨λΈ (Stripe)
|
|
188
|
+
interface StripePaymentIntent {
|
|
189
|
+
id: string;
|
|
190
|
+
amount: number; // cents
|
|
191
|
+
currency: string;
|
|
192
|
+
status: 'succeeded' | 'pending' | 'failed';
|
|
193
|
+
payment_method: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// μ°λ¦¬ λλ©μΈ λͺ¨λΈ
|
|
197
|
+
interface Payment {
|
|
198
|
+
id: PaymentId;
|
|
199
|
+
amount: Money; // μ°λ¦¬μ Money Value Object
|
|
200
|
+
status: PaymentStatus; // μ°λ¦¬μ enum
|
|
201
|
+
orderId: OrderId;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Anti-Corruption Layer
|
|
205
|
+
class StripePaymentAdapter implements PaymentGateway {
|
|
206
|
+
constructor(private stripe: Stripe) {}
|
|
207
|
+
|
|
208
|
+
async processPayment(payment: Payment): Promise<PaymentResult> {
|
|
209
|
+
// λλ©μΈ β μΈλΆ λͺ¨λΈ λ³ν
|
|
210
|
+
const intent = await this.stripe.paymentIntents.create({
|
|
211
|
+
amount: payment.amount.toCents(), // Money β cents
|
|
212
|
+
currency: payment.amount.currency.toLowerCase(),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// μΈλΆ λͺ¨λΈ β λλ©μΈ λ³ν
|
|
216
|
+
return this.toPaymentResult(intent);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private toPaymentResult(intent: StripePaymentIntent): PaymentResult {
|
|
220
|
+
return {
|
|
221
|
+
success: intent.status === 'succeeded',
|
|
222
|
+
transactionId: TransactionId.create(intent.id),
|
|
223
|
+
status: this.mapStatus(intent.status),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private mapStatus(stripeStatus: string): PaymentStatus {
|
|
228
|
+
const mapping: Record<string, PaymentStatus> = {
|
|
229
|
+
'succeeded': PaymentStatus.COMPLETED,
|
|
230
|
+
'pending': PaymentStatus.PROCESSING,
|
|
231
|
+
'failed': PaymentStatus.FAILED,
|
|
232
|
+
};
|
|
233
|
+
return mapping[stripeStatus] ?? PaymentStatus.UNKNOWN;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
]]></code>
|
|
237
|
+
</example>
|
|
238
|
+
</anticorruption-layer-pattern>
|
|
239
|
+
|
|
240
|
+
<event-storming>
|
|
241
|
+
<description>λλ©μΈ μ΄λ²€νΈλ₯Ό μ€μ¬μΌλ‘ λλ©μΈ λͺ¨λΈλ§νλ μν¬μ΅ κΈ°λ²</description>
|
|
242
|
+
<steps>
|
|
243
|
+
<step order="1">Domain Events λμΆ (μ£Όν©μ ν¬μ€νΈμ)</step>
|
|
244
|
+
<step order="2">Commands μλ³ (νλμ)</step>
|
|
245
|
+
<step order="3">Aggregates κ·Έλ£Ήν (λ
Έλμ)</step>
|
|
246
|
+
<step order="4">Bounded Context κ²½κ³ κ·Έλ¦¬κΈ°</step>
|
|
247
|
+
<step order="5">Context Map μμ±</step>
|
|
248
|
+
</steps>
|
|
249
|
+
<tip>λλ©μΈ μ λ¬Έκ°μ ν¨κ» μ§ν</tip>
|
|
250
|
+
</event-storming>
|
|
251
|
+
|
|
252
|
+
<directory-structure-by-context>
|
|
253
|
+
<description>Bounded Contextλ³ μ½λ ꡬ쑰 (λͺ¨λ
Έλ ν¬)</description>
|
|
254
|
+
<structure><![CDATA[
|
|
255
|
+
packages/
|
|
256
|
+
βββ catalog/ # Catalog Context
|
|
257
|
+
β βββ src/
|
|
258
|
+
β β βββ domain/
|
|
259
|
+
β β βββ application/
|
|
260
|
+
β β βββ infrastructure/
|
|
261
|
+
β βββ package.json
|
|
262
|
+
β
|
|
263
|
+
βββ order/ # Order Context
|
|
264
|
+
β βββ src/
|
|
265
|
+
β β βββ domain/
|
|
266
|
+
β β βββ application/
|
|
267
|
+
β β βββ infrastructure/
|
|
268
|
+
β βββ package.json
|
|
269
|
+
β
|
|
270
|
+
βββ inventory/ # Inventory Context
|
|
271
|
+
β βββ ...
|
|
272
|
+
β
|
|
273
|
+
βββ shared/ # Shared Kernel (μ΅μν)
|
|
274
|
+
βββ src/
|
|
275
|
+
β βββ value-objects/ # ProductId, Money λ± κ³΅μ VO
|
|
276
|
+
β βββ events/ # ν΅ν© μ΄λ²€νΈ μ€ν€λ§
|
|
277
|
+
βββ package.json
|
|
278
|
+
]]></structure>
|
|
279
|
+
</directory-structure-by-context>
|
|
280
|
+
|
|
281
|
+
<rules>
|
|
282
|
+
<category name="νμ">
|
|
283
|
+
<must>μ©μ΄ μ¬μ (Glossary) μμ± λ° μ μ§</must>
|
|
284
|
+
<must>μ½λμμ μ λΉμΏΌν°μ€ μΈμ΄ μ¬μ©</must>
|
|
285
|
+
<must>Bounded Context κ²½κ³ λͺ
νν μ μ</must>
|
|
286
|
+
<must>컨ν
μ€νΈ κ° ν΅μ μ λͺ
μμ μΈν°νμ΄μ€λ‘</must>
|
|
287
|
+
<must>μΈλΆ μμ€ν
μ°λ μ ACL μ¬μ©</must>
|
|
288
|
+
</category>
|
|
289
|
+
<category name="κΆμ₯">
|
|
290
|
+
<should>Core Subdomainμ μ΅κ³ 리μμ€ ν¬μ
</should>
|
|
291
|
+
<should>Generic Subdomainμ ꡬ맀/μ€νμμ€ νμ©</should>
|
|
292
|
+
<should>Event StormingμΌλ‘ λλ©μΈ λͺ¨λΈλ§</should>
|
|
293
|
+
<should>Context Map λ¬Έμν λ° μ΅μ μ μ§</should>
|
|
294
|
+
</category>
|
|
295
|
+
<category name="κΈμ§">
|
|
296
|
+
<must-not>Bounded Context κ° λλ©μΈ λͺ¨λΈ μ§μ 곡μ </must-not>
|
|
297
|
+
<must-not>νλμ Aggregateκ° μ¬λ¬ Contextμ κ±ΈμΉ¨</must-not>
|
|
298
|
+
<must-not>κΈ°μ μ©μ΄λ₯Ό λλ©μΈ λͺ¨λΈμ μ¬μ©</must-not>
|
|
299
|
+
</category>
|
|
300
|
+
</rules>
|
|
301
|
+
|
|
302
|
+
<checklist>
|
|
303
|
+
<item priority="critical">μ©μ΄ μ¬μ μ‘΄μ¬ λ° μ΅μ ν</item>
|
|
304
|
+
<item priority="critical">Bounded Context κ²½κ³ μ μλ¨</item>
|
|
305
|
+
<item priority="critical">μ½λκ° μ λΉμΏΌν°μ€ μΈμ΄ μ¬μ©</item>
|
|
306
|
+
<item priority="high">Context Map λ¬Έμν</item>
|
|
307
|
+
<item priority="high">μΈλΆ μ°λμ ACL μ μ©</item>
|
|
308
|
+
<item priority="high">Subdomain λΆλ₯ (Core/Supporting/Generic)</item>
|
|
309
|
+
<item priority="medium">Event Storming μν¬μ΅ μ§ν</item>
|
|
310
|
+
</checklist>
|
|
311
|
+
</skill>
|