vibes-plug 1.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/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/BLUEPRINT.md +125 -0
- package/CHANGELOG.md +195 -0
- package/CONTRIBUTING.md +199 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/SECURITY.md +21 -0
- package/banner.png +0 -0
- package/package.json +25 -0
- package/plugin.json +8 -0
- package/scripts/update_skills.js +75 -0
- package/skills/ai-llm-integration-expert/SKILL.md +162 -0
- package/skills/api-design-expert/SKILL.md +310 -0
- package/skills/app-analyzer-optimizer/SKILL.md +189 -0
- package/skills/asisten_ramah/SKILL.md +41 -0
- package/skills/authentication-identity-expert/SKILL.md +45 -0
- package/skills/auto-doc-updater/SKILL.md +204 -0
- package/skills/bootstrap-to-modern/SKILL.md +87 -0
- package/skills/brainstorming/SKILL.md +353 -0
- package/skills/bun-runtime-expert/SKILL.md +211 -0
- package/skills/ci-cd-devops-architect/SKILL.md +45 -0
- package/skills/cloud-hosting-expert/SKILL.md +244 -0
- package/skills/coderabbit/SKILL.md +192 -0
- package/skills/data-telemetry-expert/SKILL.md +213 -0
- package/skills/database-orm-expert/SKILL.md +294 -0
- package/skills/design-system-architect/SKILL.md +243 -0
- package/skills/e2e-testing-expert/SKILL.md +315 -0
- package/skills/event-driven-architect/SKILL.md +81 -0
- package/skills/firebase-security-expert/SKILL.md +195 -0
- package/skills/fullstack-expert/SKILL.md +202 -0
- package/skills/fullstack-expert/references/api_design_guide.md +466 -0
- package/skills/fullstack-expert/references/devops_infrastructure.md +477 -0
- package/skills/fullstack-expert/references/multi_language_backend.md +528 -0
- package/skills/fullstack-expert/references/system_design_patterns.md +358 -0
- package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -0
- package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -0
- package/skills/gemini-agent-booster/SKILL.md +135 -0
- package/skills/global-a11y-i18n-expert/SKILL.md +81 -0
- package/skills/go-programming-expert/SKILL.md +295 -0
- package/skills/hig/SKILL.md +188 -0
- package/skills/js-backend-expert/SKILL.md +192 -0
- package/skills/mcp-server-architect/SKILL.md +194 -0
- package/skills/mobile-expo-expert/SKILL.md +186 -0
- package/skills/monday-design-aesthetic/SKILL.md +67 -0
- package/skills/monorepo-architect/SKILL.md +227 -0
- package/skills/mpa-orchestrator/SKILL.md +101 -0
- package/skills/multi-agent-orchestration/SKILL.md +234 -0
- package/skills/multiple-entry-points/SKILL.md +55 -0
- package/skills/mvc-expert/SKILL.md +231 -0
- package/skills/payment-gateway-expert/SKILL.md +45 -0
- package/skills/performance-web-vitals/SKILL.md +332 -0
- package/skills/prd-architect/SKILL.md +191 -0
- package/skills/production-ready-hardener/SKILL.md +469 -0
- package/skills/production-ready-hardener/references/performance_optimization.md +441 -0
- package/skills/production-ready-hardener/references/production_checklist.md +161 -0
- package/skills/production-ready-hardener/references/security_hardening_guide.md +379 -0
- package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +875 -0
- package/skills/python-programming-expert/SKILL.md +271 -0
- package/skills/realtime-collaboration-expert/SKILL.md +45 -0
- package/skills/rust-programming-expert/SKILL.md +235 -0
- package/skills/saas-billing/SKILL.md +377 -0
- package/skills/saas-multi-tenant/SKILL.md +237 -0
- package/skills/saas-mvp-launcher/SKILL.md +231 -0
- package/skills/saas-transformer/SKILL.md +446 -0
- package/skills/saas-transformer/references/billing_integration_guide.md +401 -0
- package/skills/saas-transformer/references/feature_gating_patterns.md +137 -0
- package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -0
- package/skills/saas-transformer/scripts/saas_transformation_scanner.py +254 -0
- package/skills/scalability-clean-code/SKILL.md +229 -0
- package/skills/secure-fuzz-testing/SKILL.md +201 -0
- package/skills/senior-frontend/SKILL.md +161 -0
- package/skills/senior-frontend/references/frontend_best_practices.md +806 -0
- package/skills/senior-frontend/references/nextjs_optimization_guide.md +724 -0
- package/skills/senior-frontend/references/react_patterns.md +746 -0
- package/skills/senior-frontend/scripts/bundle_analyzer.py +407 -0
- package/skills/senior-frontend/scripts/component_generator.py +329 -0
- package/skills/senior-frontend/scripts/frontend_scaffolder.py +1005 -0
- package/skills/senior-fullstack/SKILL.md +167 -0
- package/skills/senior-fullstack/references/architecture_patterns.md +160 -0
- package/skills/senior-fullstack/references/development_workflows.md +222 -0
- package/skills/senior-fullstack/references/tech_stack_guide.md +190 -0
- package/skills/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/skills/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/skills/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/skills/seo/SKILL.md +225 -0
- package/skills/seo/references/cwv-thresholds.md +108 -0
- package/skills/seo/references/eeat-framework.md +214 -0
- package/skills/seo/references/quality-gates.md +155 -0
- package/skills/seo/references/schema-types.md +118 -0
- package/skills/seo-aeo-landing-page-writer/SKILL.md +97 -0
- package/skills/seo-geo/SKILL.md +188 -0
- package/skills/session-handoff-resume/SKILL.md +158 -0
- package/skills/skill_baru/SKILL.md +147 -0
- package/skills/spa-orchestrator/SKILL.md +288 -0
- package/skills/state-management-expert/SKILL.md +272 -0
- package/skills/supabase-migration/SKILL.md +45 -0
- package/skills/supabase-security-expert/SKILL.md +243 -0
- package/skills/tailwind-expert/SKILL.md +188 -0
- package/skills/tanstack-query-expert/SKILL.md +199 -0
- package/skills/tauri-expert/SKILL.md +97 -0
- package/skills/token-saver/SKILL.md +111 -0
- package/skills/typescript-expert/SKILL.md +279 -0
- package/skills/ui-components-expert/SKILL.md +63 -0
- package/skills/ui-ux-pro-max/SKILL.md +201 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +493 -0
- package/skills/ui-ux-pro-max/scripts/search.py +81 -0
- package/skills/ui_ux_expert/SKILL.md +114 -0
- package/skills/vibe-code-gardener/SKILL.md +173 -0
- package/skills/web-scraper/SKILL.md +205 -0
- package/skills/web-scraper/references/data-transforms.md +397 -0
- package/skills/web-scraper/references/extraction-patterns.md +475 -0
- package/skills/web-scraper/references/output-templates.md +481 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +180 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: design-system-architect
|
|
3
|
+
description: "Expert guide for designing, building, and maintaining scalable UI design systems with design tokens, headless primitives (Radix/Base UI), Tailwind v4 @theme, and WCAG 2.2 accessibility in English and Indonesian."
|
|
4
|
+
author: "Roedy Rustam"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Design System Architect (2026 Edition — shadcn/ui Registry)
|
|
8
|
+
|
|
9
|
+
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<a name="english"></a>
|
|
14
|
+
## English
|
|
15
|
+
|
|
16
|
+
### Description
|
|
17
|
+
Expert guide for building and maintaining scalable UI design systems. Covers design tokens with OKLCH and Tailwind v4 `@theme`, headless component primitives (Radix UI, **Base UI 1.x**), **shadcn/ui registry** for distributable component libraries, CVA for type-safe variants, and WCAG 2.2 accessibility compliance.
|
|
18
|
+
|
|
19
|
+
### Trigger Conditions
|
|
20
|
+
- Building a shared UI component library from scratch.
|
|
21
|
+
- Setting up design tokens (colors, typography, spacing) with Tailwind v4.
|
|
22
|
+
- Using headless primitives (Radix UI, Base UI 1.x) with custom styling.
|
|
23
|
+
- Distributing components via the **shadcn/ui registry** format.
|
|
24
|
+
- Auditing a component library for WCAG 2.2 accessibility compliance.
|
|
25
|
+
|
|
26
|
+
### Design Token Foundation (Tailwind v4 + OKLCH)
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
/* packages/ui/src/tokens.css */
|
|
30
|
+
@import "tailwindcss";
|
|
31
|
+
|
|
32
|
+
@theme {
|
|
33
|
+
/* --- Color System (OKLCH for wide-gamut P3 displays) --- */
|
|
34
|
+
/* Brand */
|
|
35
|
+
--color-brand-50: oklch(97% 0.015 250);
|
|
36
|
+
--color-brand-100: oklch(93% 0.04 250);
|
|
37
|
+
--color-brand-500: oklch(55% 0.22 250);
|
|
38
|
+
--color-brand-700: oklch(40% 0.18 250);
|
|
39
|
+
--color-brand-900: oklch(20% 0.10 250);
|
|
40
|
+
|
|
41
|
+
/* Semantic (maps to brand in light/dark automatically) */
|
|
42
|
+
--color-primary: var(--color-brand-500);
|
|
43
|
+
--color-primary-fg: oklch(100% 0 0); /* White */
|
|
44
|
+
--color-surface: oklch(100% 0 0); /* White */
|
|
45
|
+
--color-surface-2: oklch(97% 0.005 250);
|
|
46
|
+
--color-border: oklch(90% 0.01 250);
|
|
47
|
+
--color-text: oklch(15% 0.02 250);
|
|
48
|
+
--color-text-muted: oklch(50% 0.015 250);
|
|
49
|
+
--color-destructive: oklch(55% 0.22 25); /* Red */
|
|
50
|
+
|
|
51
|
+
/* --- Typography --- */
|
|
52
|
+
--font-sans: "Inter Variable", "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
53
|
+
--font-mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
|
|
54
|
+
--font-size-xs: 0.75rem;
|
|
55
|
+
--font-size-sm: 0.875rem;
|
|
56
|
+
--font-size-base: 1rem;
|
|
57
|
+
--font-size-lg: 1.125rem;
|
|
58
|
+
--font-size-xl: 1.25rem;
|
|
59
|
+
--font-size-2xl: 1.5rem;
|
|
60
|
+
--font-size-3xl: 1.875rem;
|
|
61
|
+
--font-size-4xl: 2.25rem;
|
|
62
|
+
|
|
63
|
+
/* --- Spacing & Radius --- */
|
|
64
|
+
--radius-sm: 0.25rem;
|
|
65
|
+
--radius-md: 0.5rem;
|
|
66
|
+
--radius-lg: 0.75rem;
|
|
67
|
+
--radius-xl: 1rem;
|
|
68
|
+
--radius-full: 9999px;
|
|
69
|
+
|
|
70
|
+
/* --- Animation --- */
|
|
71
|
+
--animate-fade-in: fade-in 0.2s ease-out;
|
|
72
|
+
--animate-slide-up: slide-up 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
73
|
+
--animate-scale-in: scale-in 0.2s ease-out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Dark mode tokens */
|
|
77
|
+
@variant dark {
|
|
78
|
+
:root {
|
|
79
|
+
--color-surface: oklch(10% 0.015 250);
|
|
80
|
+
--color-surface-2: oklch(15% 0.015 250);
|
|
81
|
+
--color-border: oklch(25% 0.02 250);
|
|
82
|
+
--color-text: oklch(95% 0.005 250);
|
|
83
|
+
--color-text-muted: oklch(60% 0.01 250);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Component Architecture
|
|
89
|
+
|
|
90
|
+
#### Headless + Styled Pattern (Base UI 1.x / Radix UI)
|
|
91
|
+
Use headless primitives for accessibility, apply styles via Tailwind + CVA:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// packages/ui/src/button.tsx
|
|
95
|
+
import * as React from 'react';
|
|
96
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
97
|
+
import { cn } from '@/lib/utils';
|
|
98
|
+
|
|
99
|
+
const buttonVariants = cva(
|
|
100
|
+
// Base styles
|
|
101
|
+
'inline-flex items-center justify-center gap-2 rounded-md font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 select-none',
|
|
102
|
+
{
|
|
103
|
+
variants: {
|
|
104
|
+
variant: {
|
|
105
|
+
default: 'bg-primary text-primary-fg shadow-sm hover:brightness-110 active:brightness-90',
|
|
106
|
+
secondary: 'bg-surface-2 text-text border border-border hover:bg-surface hover:border-brand-300',
|
|
107
|
+
destructive: 'bg-destructive text-white hover:brightness-110',
|
|
108
|
+
ghost: 'hover:bg-surface-2 text-text',
|
|
109
|
+
link: 'text-primary underline-offset-4 hover:underline p-0 h-auto',
|
|
110
|
+
},
|
|
111
|
+
size: {
|
|
112
|
+
sm: 'h-8 px-3 text-xs',
|
|
113
|
+
md: 'h-9 px-4 text-sm',
|
|
114
|
+
lg: 'h-11 px-6 text-base',
|
|
115
|
+
icon: 'h-9 w-9',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
defaultVariants: { variant: 'default', size: 'md' },
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
export interface ButtonProps
|
|
123
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
124
|
+
VariantProps<typeof buttonVariants> {
|
|
125
|
+
asChild?: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
129
|
+
({ className, variant, size, ...props }, ref) => (
|
|
130
|
+
<button
|
|
131
|
+
ref={ref}
|
|
132
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
133
|
+
{...props}
|
|
134
|
+
/>
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
Button.displayName = 'Button';
|
|
138
|
+
|
|
139
|
+
export { Button, buttonVariants };
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### shadcn/ui Registry — Distributable Components (2026)
|
|
143
|
+
|
|
144
|
+
shadcn/ui v2 introduces a **registry** system — distribute your components as a shareable library that others can `npx shadcn add` into their projects:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
// registry.json — defines your component library
|
|
148
|
+
{
|
|
149
|
+
"$schema": "https://ui.shadcn.com/schema/registry.json",
|
|
150
|
+
"name": "my-ui",
|
|
151
|
+
"homepage": "https://ui.myapp.com",
|
|
152
|
+
"items": [
|
|
153
|
+
{
|
|
154
|
+
"name": "button",
|
|
155
|
+
"type": "registry:ui",
|
|
156
|
+
"title": "Button",
|
|
157
|
+
"description": "Multi-variant button component with CVA",
|
|
158
|
+
"files": [
|
|
159
|
+
{ "path": "registry/ui/button.tsx", "type": "registry:ui" }
|
|
160
|
+
],
|
|
161
|
+
"tailwind": {
|
|
162
|
+
"config": {
|
|
163
|
+
"theme": {
|
|
164
|
+
"extend": {
|
|
165
|
+
"colors": { "primary": "hsl(var(--primary))" }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Users install your components directly
|
|
177
|
+
npx shadcn add https://ui.myapp.com/registry.json button
|
|
178
|
+
npx shadcn add https://ui.myapp.com/registry.json card dialog
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Base UI 1.x — Unstyled Accessibility Primitives
|
|
182
|
+
Base UI (from MUI team) is the 2026 alternative to Radix UI with React 19 native support:
|
|
183
|
+
```typescript
|
|
184
|
+
import { Dialog, Button, Select } from '@base-ui-components/react';
|
|
185
|
+
|
|
186
|
+
// Fully unstyled — apply any className/Tailwind styles
|
|
187
|
+
<Dialog.Root>
|
|
188
|
+
<Dialog.Trigger render={<Button />}>Open Dialog</Dialog.Trigger>
|
|
189
|
+
<Dialog.Portal>
|
|
190
|
+
<Dialog.Backdrop className="fixed inset-0 bg-black/50 animate-fade-in" />
|
|
191
|
+
<Dialog.Popup className="fixed inset-0 m-auto h-fit max-w-md rounded-xl bg-surface p-6 shadow-xl animate-slide-up">
|
|
192
|
+
<Dialog.Title className="text-lg font-semibold">Title</Dialog.Title>
|
|
193
|
+
<Dialog.Close className="absolute right-4 top-4">✕</Dialog.Close>
|
|
194
|
+
</Dialog.Popup>
|
|
195
|
+
</Dialog.Portal>
|
|
196
|
+
</Dialog.Root>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### WCAG 2.2 Accessibility Checklist
|
|
200
|
+
- [ ] All interactive elements have visible focus indicators (`ring-2`).
|
|
201
|
+
- [ ] Color contrast ≥ 4.5:1 (text), ≥ 3:1 (large text / UI components).
|
|
202
|
+
- [ ] All images have `alt` text; decorative images have `alt=""`.
|
|
203
|
+
- [ ] All form inputs have associated `<label>` elements.
|
|
204
|
+
- [ ] Keyboard navigation works for all interactions (Tab, Enter, Space, Escape, Arrow keys).
|
|
205
|
+
- [ ] `aria-label` or `aria-labelledby` on icon buttons.
|
|
206
|
+
- [ ] Modals trap focus and restore it on close.
|
|
207
|
+
- [ ] No content relies on color alone to convey information.
|
|
208
|
+
- [ ] Touch targets ≥ 24×24px (WCAG 2.2 new requirement).
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
<a name="bahasa-indonesia"></a>
|
|
213
|
+
## Bahasa Indonesia
|
|
214
|
+
|
|
215
|
+
### Deskripsi
|
|
216
|
+
Panduan ahli untuk membangun dan memelihara design system UI yang skalabel. Mencakup design token dengan OKLCH dan `@theme` Tailwind v4, primitif komponen headless (Radix UI, **Base UI 1.x**), **registry shadcn/ui** untuk distribusi library komponen, CVA untuk varian type-safe, dan kepatuhan aksesibilitas WCAG 2.2.
|
|
217
|
+
|
|
218
|
+
### Kondisi Pemicu
|
|
219
|
+
- Membangun library komponen UI bersama dari awal.
|
|
220
|
+
- Menyiapkan design token (warna, tipografi, spacing) dengan Tailwind v4.
|
|
221
|
+
- Menggunakan primitif headless (Radix UI, Base UI 1.x) dengan styling kustom.
|
|
222
|
+
- Mendistribusikan komponen via format **registry shadcn/ui**.
|
|
223
|
+
- Mengaudit library komponen untuk kepatuhan aksesibilitas WCAG 2.2.
|
|
224
|
+
|
|
225
|
+
### Fondasi Design Token (Tailwind v4 + OKLCH)
|
|
226
|
+
Definisikan semua token di CSS menggunakan direktif `@theme`. Gunakan warna OKLCH untuk tampilan P3 wide-gamut. Definisikan token semantik (`--color-primary`, `--color-surface`, `--color-border`) yang secara otomatis beradaptasi antara mode terang/gelap melalui `@variant dark`.
|
|
227
|
+
|
|
228
|
+
### Arsitektur Komponen
|
|
229
|
+
Gunakan pola Headless + Styled: primitif headless (Base UI/Radix) untuk aksesibilitas, gaya melalui Tailwind + CVA (class-variance-authority) untuk varian type-safe.
|
|
230
|
+
|
|
231
|
+
### Registry shadcn/ui — Komponen yang Dapat Didistribusikan (2026)
|
|
232
|
+
shadcn/ui v2 memperkenalkan sistem registry — distribusikan komponen Anda sebagai library yang dapat dibagikan sehingga orang lain dapat menginstalnya dengan `npx shadcn add [url] [komponen]`.
|
|
233
|
+
|
|
234
|
+
### Base UI 1.x — Primitif Aksesibilitas Tanpa Gaya
|
|
235
|
+
Base UI (dari tim MUI) adalah alternatif Radix UI untuk 2026 dengan dukungan native React 19. Sepenuhnya tanpa gaya — terapkan className/Tailwind apapun.
|
|
236
|
+
|
|
237
|
+
### Checklist Aksesibilitas WCAG 2.2
|
|
238
|
+
- Semua elemen interaktif memiliki indikator fokus yang terlihat.
|
|
239
|
+
- Kontras warna ≥ 4.5:1 (teks), ≥ 3:1 (komponen UI).
|
|
240
|
+
- Semua input form memiliki elemen `<label>` terkait.
|
|
241
|
+
- Navigasi keyboard berfungsi untuk semua interaksi.
|
|
242
|
+
- Modal menjebak fokus dan memulihkannya saat ditutup.
|
|
243
|
+
- Target sentuh ≥ 24×24px (persyaratan baru WCAG 2.2).
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-testing-expert
|
|
3
|
+
description: "Expert guide for End-to-End (E2E) testing with Playwright, unit/integration testing with Vitest, and CI/CD automated testing pipeline setup / Panduan ahli pengujian End-to-End (E2E) dengan Playwright, pengujian unit/integrasi dengan Vitest, dan otomatisasi CI/CD."
|
|
4
|
+
author: "Roedy Rustam"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# E2E Testing Expert (Playwright 1.49+ / Vitest 3 Edition)
|
|
8
|
+
|
|
9
|
+
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<a name="english"></a>
|
|
14
|
+
## English
|
|
15
|
+
|
|
16
|
+
### Description
|
|
17
|
+
Expert guide for building comprehensive automated test suites using **Playwright 1.49+** (E2E + component testing), **Vitest 3** (unit/integration), and robust CI/CD pipelines. Covers modern testing patterns for Next.js 15, React 19, and API backends.
|
|
18
|
+
|
|
19
|
+
### Trigger Conditions
|
|
20
|
+
- Writing E2E tests for web applications with Playwright.
|
|
21
|
+
- Writing unit or integration tests with Vitest.
|
|
22
|
+
- Setting up a full automated testing pipeline in GitHub Actions / GitLab CI.
|
|
23
|
+
- Testing React components in isolation with Playwright Component Testing.
|
|
24
|
+
- Mocking external services (APIs, databases) in tests.
|
|
25
|
+
- Setting up visual regression tests.
|
|
26
|
+
|
|
27
|
+
### Playwright 1.49+ — Key Updates
|
|
28
|
+
|
|
29
|
+
#### New in 2026
|
|
30
|
+
- **`aria-snapshot`**: Assert the accessibility tree as a snapshot — more semantic than DOM snapshots.
|
|
31
|
+
- **`page.addLocatorHandler()`**: Handle dynamic UI (modals, cookie banners) automatically.
|
|
32
|
+
- **WebSocket testing**: Built-in `page.expectWebSocketEvent()`.
|
|
33
|
+
- **Playwright MCP Server**: Expose a Playwright browser to AI agents via MCP tools.
|
|
34
|
+
|
|
35
|
+
#### Project Configuration
|
|
36
|
+
```typescript
|
|
37
|
+
// playwright.config.ts
|
|
38
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
39
|
+
|
|
40
|
+
export default defineConfig({
|
|
41
|
+
testDir: './tests/e2e',
|
|
42
|
+
fullyParallel: true,
|
|
43
|
+
forbidOnly: !!process.env.CI,
|
|
44
|
+
retries: process.env.CI ? 2 : 0,
|
|
45
|
+
workers: process.env.CI ? 4 : undefined,
|
|
46
|
+
reporter: [
|
|
47
|
+
['html', { open: 'never' }],
|
|
48
|
+
['junit', { outputFile: 'results.xml' }],
|
|
49
|
+
],
|
|
50
|
+
use: {
|
|
51
|
+
baseURL: process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000',
|
|
52
|
+
trace: 'on-first-retry',
|
|
53
|
+
screenshot: 'only-on-failure',
|
|
54
|
+
video: 'on-first-retry',
|
|
55
|
+
},
|
|
56
|
+
projects: [
|
|
57
|
+
// Setup: seed test DB
|
|
58
|
+
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
|
|
59
|
+
{
|
|
60
|
+
name: 'chromium',
|
|
61
|
+
use: { ...devices['Desktop Chrome'] },
|
|
62
|
+
dependencies: ['setup'],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'mobile-chrome',
|
|
66
|
+
use: { ...devices['Pixel 7'] },
|
|
67
|
+
dependencies: ['setup'],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
webServer: {
|
|
71
|
+
command: 'npm run dev',
|
|
72
|
+
url: 'http://localhost:3000',
|
|
73
|
+
reuseExistingServer: !process.env.CI,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Page Object Model (POM)
|
|
79
|
+
```typescript
|
|
80
|
+
// tests/e2e/pages/LoginPage.ts
|
|
81
|
+
import { type Page, type Locator, expect } from '@playwright/test';
|
|
82
|
+
|
|
83
|
+
export class LoginPage {
|
|
84
|
+
readonly page: Page;
|
|
85
|
+
readonly emailInput: Locator;
|
|
86
|
+
readonly passwordInput: Locator;
|
|
87
|
+
readonly submitButton: Locator;
|
|
88
|
+
|
|
89
|
+
constructor(page: Page) {
|
|
90
|
+
this.page = page;
|
|
91
|
+
this.emailInput = page.getByLabel('Email');
|
|
92
|
+
this.passwordInput = page.getByLabel('Password');
|
|
93
|
+
this.submitButton = page.getByRole('button', { name: 'Sign in' });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async goto() {
|
|
97
|
+
await this.page.goto('/login');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async login(email: string, password: string) {
|
|
101
|
+
await this.emailInput.fill(email);
|
|
102
|
+
await this.passwordInput.fill(password);
|
|
103
|
+
await this.submitButton.click();
|
|
104
|
+
await this.page.waitForURL('/dashboard');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### API Mocking with `page.route()`
|
|
110
|
+
```typescript
|
|
111
|
+
test('shows error when API fails', async ({ page }) => {
|
|
112
|
+
// Intercept and mock API response
|
|
113
|
+
await page.route('**/api/users', (route) => {
|
|
114
|
+
route.fulfill({
|
|
115
|
+
status: 500,
|
|
116
|
+
body: JSON.stringify({ error: 'Internal server error' }),
|
|
117
|
+
contentType: 'application/json',
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
await page.goto('/users');
|
|
122
|
+
await expect(page.getByRole('alert')).toContainText('Something went wrong');
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### Accessibility Assertions (aria-snapshot)
|
|
127
|
+
```typescript
|
|
128
|
+
test('navigation is accessible', async ({ page }) => {
|
|
129
|
+
await page.goto('/');
|
|
130
|
+
|
|
131
|
+
// Assert accessibility tree structure
|
|
132
|
+
await expect(page.locator('nav')).toMatchAriaSnapshot(`
|
|
133
|
+
- navigation:
|
|
134
|
+
- link "Home"
|
|
135
|
+
- link "Products"
|
|
136
|
+
- link "Pricing"
|
|
137
|
+
- link "Sign in"
|
|
138
|
+
`);
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Auth State Reuse (storageState)
|
|
143
|
+
```typescript
|
|
144
|
+
// tests/e2e/auth.setup.ts
|
|
145
|
+
import { test as setup } from '@playwright/test';
|
|
146
|
+
|
|
147
|
+
setup('authenticate', async ({ page }) => {
|
|
148
|
+
await page.goto('/login');
|
|
149
|
+
await page.getByLabel('Email').fill('test@example.com');
|
|
150
|
+
await page.getByLabel('Password').fill('password');
|
|
151
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
152
|
+
await page.waitForURL('/dashboard');
|
|
153
|
+
|
|
154
|
+
// Save session state for reuse in all tests
|
|
155
|
+
await page.context().storageState({ path: 'playwright/.auth/user.json' });
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// tests/e2e/dashboard.spec.ts
|
|
159
|
+
import { test } from '@playwright/test';
|
|
160
|
+
|
|
161
|
+
test.use({ storageState: 'playwright/.auth/user.json' });
|
|
162
|
+
|
|
163
|
+
test('dashboard loads correctly', async ({ page }) => {
|
|
164
|
+
await page.goto('/dashboard');
|
|
165
|
+
// Already authenticated — no login needed
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Vitest 3 — Unit & Integration Testing
|
|
170
|
+
|
|
171
|
+
#### Configuration
|
|
172
|
+
```typescript
|
|
173
|
+
// vitest.config.ts
|
|
174
|
+
import { defineConfig } from 'vitest/config';
|
|
175
|
+
import react from '@vitejs/plugin-react';
|
|
176
|
+
|
|
177
|
+
export default defineConfig({
|
|
178
|
+
plugins: [react()],
|
|
179
|
+
test: {
|
|
180
|
+
environment: 'jsdom',
|
|
181
|
+
globals: true,
|
|
182
|
+
setupFiles: ['./tests/setup.ts'],
|
|
183
|
+
coverage: {
|
|
184
|
+
provider: 'v8',
|
|
185
|
+
reporter: ['text', 'lcov', 'html'],
|
|
186
|
+
thresholds: { lines: 80, functions: 80 },
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### React Component Testing
|
|
193
|
+
```typescript
|
|
194
|
+
import { render, screen, userEvent } from '@testing-library/react';
|
|
195
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
196
|
+
import { Counter } from './Counter';
|
|
197
|
+
|
|
198
|
+
describe('Counter', () => {
|
|
199
|
+
it('increments count on button click', async () => {
|
|
200
|
+
const user = userEvent.setup();
|
|
201
|
+
render(<Counter initialCount={0} />);
|
|
202
|
+
|
|
203
|
+
await user.click(screen.getByRole('button', { name: 'Increment' }));
|
|
204
|
+
|
|
205
|
+
expect(screen.getByText('Count: 1')).toBeInTheDocument();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('calls onMaxReached when limit hit', async () => {
|
|
209
|
+
const onMaxReached = vi.fn();
|
|
210
|
+
const user = userEvent.setup();
|
|
211
|
+
render(<Counter initialCount={9} max={10} onMaxReached={onMaxReached} />);
|
|
212
|
+
|
|
213
|
+
await user.click(screen.getByRole('button', { name: 'Increment' }));
|
|
214
|
+
|
|
215
|
+
expect(onMaxReached).toHaveBeenCalledOnce();
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### Server Action / API Route Testing
|
|
221
|
+
```typescript
|
|
222
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
223
|
+
import { testClient } from 'hono/testing';
|
|
224
|
+
import { app } from '../src/server';
|
|
225
|
+
import { resetTestDb } from './helpers/db';
|
|
226
|
+
|
|
227
|
+
describe('POST /api/users', () => {
|
|
228
|
+
beforeEach(resetTestDb);
|
|
229
|
+
|
|
230
|
+
it('creates a new user', async () => {
|
|
231
|
+
const client = testClient(app);
|
|
232
|
+
const res = await client.api.users.$post({
|
|
233
|
+
json: { name: 'Alice', email: 'alice@example.com' },
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(res.status).toBe(201);
|
|
237
|
+
const data = await res.json();
|
|
238
|
+
expect(data.email).toBe('alice@example.com');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### GitHub Actions CI Pipeline
|
|
244
|
+
```yaml
|
|
245
|
+
# .github/workflows/test.yml
|
|
246
|
+
name: CI Tests
|
|
247
|
+
|
|
248
|
+
on: [push, pull_request]
|
|
249
|
+
|
|
250
|
+
jobs:
|
|
251
|
+
unit:
|
|
252
|
+
runs-on: ubuntu-latest
|
|
253
|
+
steps:
|
|
254
|
+
- uses: actions/checkout@v4
|
|
255
|
+
- uses: actions/setup-node@v4
|
|
256
|
+
with: { node-version: '22', cache: 'pnpm' }
|
|
257
|
+
- run: pnpm install --frozen-lockfile
|
|
258
|
+
- run: pnpm run typecheck
|
|
259
|
+
- run: pnpm run test:unit --coverage
|
|
260
|
+
|
|
261
|
+
e2e:
|
|
262
|
+
runs-on: ubuntu-latest
|
|
263
|
+
steps:
|
|
264
|
+
- uses: actions/checkout@v4
|
|
265
|
+
- uses: actions/setup-node@v4
|
|
266
|
+
with: { node-version: '22', cache: 'pnpm' }
|
|
267
|
+
- run: pnpm install --frozen-lockfile
|
|
268
|
+
- run: pnpm exec playwright install --with-deps chromium
|
|
269
|
+
- run: pnpm run test:e2e
|
|
270
|
+
- uses: actions/upload-artifact@v4
|
|
271
|
+
if: failure()
|
|
272
|
+
with:
|
|
273
|
+
name: playwright-report
|
|
274
|
+
path: playwright-report/
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
<a name="bahasa-indonesia"></a>
|
|
280
|
+
## Bahasa Indonesia
|
|
281
|
+
|
|
282
|
+
### Deskripsi
|
|
283
|
+
Panduan ahli untuk membangun test suite otomatis yang komprehensif menggunakan **Playwright 1.49+** (E2E + component testing), **Vitest 3** (unit/integrasi), dan pipeline CI/CD yang kuat. Mencakup pola pengujian modern untuk Next.js 15, React 19, dan backend API.
|
|
284
|
+
|
|
285
|
+
### Kondisi Pemicu
|
|
286
|
+
- Menulis E2E test untuk aplikasi web dengan Playwright.
|
|
287
|
+
- Menulis unit atau integration test dengan Vitest.
|
|
288
|
+
- Menyiapkan pipeline pengujian otomatis di GitHub Actions / GitLab CI.
|
|
289
|
+
- Menguji komponen React secara terisolasi.
|
|
290
|
+
- Mocking layanan eksternal (API, database) dalam test.
|
|
291
|
+
- Menyiapkan visual regression test.
|
|
292
|
+
|
|
293
|
+
### Playwright 1.49+ — Fitur Baru 2026
|
|
294
|
+
- **`aria-snapshot`**: Assert accessibility tree sebagai snapshot.
|
|
295
|
+
- **`page.addLocatorHandler()`**: Tangani UI dinamis (modal, cookie banner) secara otomatis.
|
|
296
|
+
- **WebSocket testing**: `page.expectWebSocketEvent()` bawaan.
|
|
297
|
+
- **Playwright MCP Server**: Ekspos browser Playwright ke agen AI via MCP.
|
|
298
|
+
|
|
299
|
+
### Page Object Model (POM)
|
|
300
|
+
Enkapsulasi selektor dan tindakan halaman dalam kelas terpisah untuk mengurangi duplikasi dan meningkatkan keterbacaan test.
|
|
301
|
+
|
|
302
|
+
### Mocking API dengan `page.route()`
|
|
303
|
+
Gunakan `page.route()` untuk mencegat permintaan jaringan dan mengembalikan respons palsu — mengisolasi frontend dari backend dalam E2E test.
|
|
304
|
+
|
|
305
|
+
### Aksesibilitas dengan aria-snapshot
|
|
306
|
+
Gunakan `toMatchAriaSnapshot()` untuk memvalidasi struktur aksesibilitas navigasi, form, dan komponen interaktif — lebih semantis dari snapshot DOM biasa.
|
|
307
|
+
|
|
308
|
+
### Auth State Reuse (storageState)
|
|
309
|
+
Gunakan `storageState` untuk menyimpan state sesi setelah login sekali dan menggunakannya kembali di semua test yang memerlukan autentikasi — menghemat waktu secara signifikan.
|
|
310
|
+
|
|
311
|
+
### Vitest 3 — Unit & Integration Testing
|
|
312
|
+
Vitest 3 adalah test runner yang cepat berbasis Vite dengan kompatibilitas Jest penuh. Gunakan untuk unit test React component, server action, dan logika bisnis.
|
|
313
|
+
|
|
314
|
+
### Pipeline CI GitHub Actions
|
|
315
|
+
Pisahkan job `unit` dan `e2e` untuk feedback paralel yang lebih cepat. Upload `playwright-report` sebagai artifact saat gagal untuk debugging.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: event-driven-architect
|
|
3
|
+
description: "Expert guide for microservices, message queues, Event Sourcing, and high-scale backend architectures / Panduan ahli untuk arsitektur microservices, antrean pesan, dan backend skala tinggi."
|
|
4
|
+
author: "Roedy Rustam"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Event-Driven Architecture Expert
|
|
8
|
+
|
|
9
|
+
[English](#english) | [Bahasa Indonesia](#bahasa-indonesia)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
<a name="english"></a>
|
|
14
|
+
## English
|
|
15
|
+
|
|
16
|
+
### Description
|
|
17
|
+
This skill provides architectural guidance for breaking down large monolithic applications into scalable, decoupled services. It focuses on Event-Driven Architecture (EDA), message brokers, and advanced patterns like Event Sourcing and CQRS (Command Query Responsibility Segregation).
|
|
18
|
+
|
|
19
|
+
### Trigger Conditions
|
|
20
|
+
- Scaling a system that handles extremely high throughput or traffic spikes.
|
|
21
|
+
- Implementing background jobs, asynchronous workers, or heavy data processing.
|
|
22
|
+
- Transitioning from a monolith to microservices.
|
|
23
|
+
- Integrating message brokers like RabbitMQ, Apache Kafka, AWS SQS, or Redis Pub/Sub.
|
|
24
|
+
- Resolving distributed transaction issues (e.g., handling the Saga pattern).
|
|
25
|
+
|
|
26
|
+
### Core Architecture Guidelines
|
|
27
|
+
|
|
28
|
+
#### 1. Decoupling via Events
|
|
29
|
+
Do not use synchronous HTTP calls between microservices for state mutations. It creates tightly coupled systems that fail in cascades.
|
|
30
|
+
- **Publisher/Subscriber**: When Service A does something, it publishes an event (e.g., `UserCreated`). Service B and Service C listen to this event and react asynchronously.
|
|
31
|
+
- Use **RabbitMQ** or **AWS SQS** for standard task queues (where order and exact-once delivery might be managed).
|
|
32
|
+
- Use **Apache Kafka** or **Redpanda** for high-throughput event streaming where logs need to be replayed.
|
|
33
|
+
|
|
34
|
+
#### 2. The Saga Pattern (Distributed Transactions)
|
|
35
|
+
In microservices, you cannot use simple database transactions (ACID) across different databases.
|
|
36
|
+
- Use the **Saga Pattern** to coordinate multiple operations. If Step 3 in a 4-step process fails, the system must publish "compensating events" to undo Steps 1 and 2 in the other services.
|
|
37
|
+
|
|
38
|
+
#### 3. CQRS (Command Query Responsibility Segregation)
|
|
39
|
+
For high-read applications (like social media or analytics dashboards), split the read operations from the write operations.
|
|
40
|
+
- **Commands**: Mutate state (write database).
|
|
41
|
+
- **Queries**: Read state (read replica or materialized views/Elasticsearch).
|
|
42
|
+
- Events synchronize the Write DB with the Read DB.
|
|
43
|
+
|
|
44
|
+
#### 4. Event Sourcing
|
|
45
|
+
Instead of storing just the current state of an entity (e.g., `AccountBalance = $100`), store the *history of events* that led to that state (`Deposited $150`, `Withdrew $50`). The current state is derived by replaying the events. This is mandatory for financial and highly auditable systems.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
<a name="bahasa-indonesia"></a>
|
|
50
|
+
## Bahasa Indonesia
|
|
51
|
+
|
|
52
|
+
### Deskripsi
|
|
53
|
+
Skill ini memberikan panduan arsitektural untuk memecah aplikasi monolit yang besar menjadi layanan yang terdesentralisasi (*decoupled*) dan dapat diskalakan. Fokus utamanya adalah pada *Event-Driven Architecture* (Arsitektur Berbasis Peristiwa), *message brokers* (pialang pesan), serta pola lanjutan seperti *Event Sourcing* dan CQRS (*Command Query Responsibility Segregation*).
|
|
54
|
+
|
|
55
|
+
### Kondisi Pemicu
|
|
56
|
+
- Menskalakan sistem untuk menangani lonjakan *traffic* atau volume data yang sangat tinggi.
|
|
57
|
+
- Mengimplementasikan pekerjaan latar belakang (*background jobs*), *worker asinkron*, atau pemrosesan data berat.
|
|
58
|
+
- Bertransisi dari arsitektur monolit ke layanan mikro (*microservices*).
|
|
59
|
+
- Mengintegrasikan *message brokers* seperti RabbitMQ, Apache Kafka, AWS SQS, atau Redis Pub/Sub.
|
|
60
|
+
- Menyelesaikan masalah transaksi terdistribusi (misalnya menerapkan pola Saga).
|
|
61
|
+
|
|
62
|
+
### Panduan Arsitektur Inti
|
|
63
|
+
|
|
64
|
+
#### 1. Desentralisasi melalui Event
|
|
65
|
+
Jangan gunakan panggilan HTTP yang sinkron antar *microservices* untuk memutasi data. Hal ini menciptakan sistem yang saling bergantung ketat (*tightly coupled*) dan bisa menyebabkan kegagalan beruntun (*cascading failures*).
|
|
66
|
+
- **Publisher/Subscriber**: Saat Layanan A melakukan sesuatu, ia menerbitkan sebuah event (misalnya `UserCreated`). Layanan B dan C akan mendengarkan event ini dan bereaksi secara asinkron.
|
|
67
|
+
- Gunakan **RabbitMQ** atau **AWS SQS** untuk antrean tugas standar.
|
|
68
|
+
- Gunakan **Apache Kafka** atau **Redpanda** untuk *streaming event* throughput tinggi yang memerlukan *replay* log.
|
|
69
|
+
|
|
70
|
+
#### 2. Pola Saga (Transaksi Terdistribusi)
|
|
71
|
+
Dalam arsitektur *microservices*, Anda tidak dapat menggunakan transaksi database ACID standar lintas database yang berbeda.
|
|
72
|
+
- Gunakan **Saga Pattern** untuk mengoordinasikan beberapa operasi. Jika Langkah 3 dalam proses 4-langkah gagal, sistem harus menerbitkan "event kompensasi" (pembatalan) untuk membatalkan Langkah 1 dan 2 pada layanan sebelumnya.
|
|
73
|
+
|
|
74
|
+
#### 3. CQRS (Command Query Responsibility Segregation)
|
|
75
|
+
Untuk aplikasi dengan tingkat pembacaan yang sangat tinggi (seperti dasbor analitik), pisahkan operasi baca dari operasi tulis.
|
|
76
|
+
- **Command**: Memutasi state/data (Database Tulis).
|
|
77
|
+
- **Query**: Membaca state (Read Replica, Materialized Views, atau Elasticsearch).
|
|
78
|
+
- Event bertugas menyinkronkan Database Tulis ke Database Baca di latar belakang.
|
|
79
|
+
|
|
80
|
+
#### 4. Event Sourcing
|
|
81
|
+
Daripada hanya menyimpan kondisi terbaru sebuah data (mis. `SaldoAkun = Rp1.000.000`), simpan *sejarah lengkap peristiwa* yang mengarah ke saldo tersebut (`Setor Rp1.500.000`, `Tarik Rp500.000`). Kondisi saat ini diturunkan (dikalkulasi) dengan memutar ulang (*replaying*) event tersebut. Pola ini wajib untuk sistem finansial dan sistem yang membutuhkan jejak audit tingkat tinggi.
|