tzurot-architecture
1
总安装量
1
周安装量
#51310
全站排名
安装命令
npx skills add https://github.com/lbds137/tzurot --skill tzurot-architecture
Agent 安装分布
opencode
1
Skill 文档
Tzurot v3 Architecture
Use this skill when: Adding new features, deciding where code belongs, designing system interactions, or refactoring service boundaries.
Quick Reference
Discord User
â
bot-client (Discord.js)
â HTTP
api-gateway (Express + BullMQ)
â Redis Queue
ai-worker (AI + pgvector)
â
OpenRouter/Gemini API
Core Principles
- Simple, clean classes – No DDD over-engineering (learned from v2)
- Clear service boundaries – Each service has single responsibility
- No circular dependencies – Services can’t import from each other
- Shared code in common-types – Cross-service types, utils, services
- Constructor injection – Simple dependency passing, no DI containers
Three Microservices
| Service | Responsibility | Does | Does NOT |
|---|---|---|---|
| bot-client | Discord interface | Events, webhooks, commands, formatting | Business logic, AI calls, direct DB |
| api-gateway | HTTP API + queue | Endpoints, validation, job creation | AI processing, Discord interaction |
| ai-worker | AI + memory | Jobs, memory, AI calls, embeddings | HTTP endpoints, Discord interaction |
Where to Put New Code
| Type | Location |
|---|---|
| Webhook/message formatting | bot-client/ |
| Slash commands | bot-client/commands/ |
| HTTP endpoints | api-gateway/routes/ |
| Job creation | api-gateway/queue.ts |
| AI provider clients | ai-worker/providers/ |
| Memory/embeddings | ai-worker/services/ |
| Shared types/constants | common-types/ |
| Discord type guards | common-types/types/ |
Autocomplete Utilities (CRITICAL)
ALWAYS check for existing utilities before writing autocomplete handlers.
Available in bot-client/src/utils/autocomplete/:
| Utility | Purpose | Option Names |
|---|---|---|
handlePersonalityAutocomplete |
Personality selection | personality, character |
handlePersonaAutocomplete |
Profile/persona selection | profile, persona |
// â
GOOD - Delegate to shared utility
import { handlePersonalityAutocomplete } from '../../utils/autocomplete/personalityAutocomplete.js';
await handlePersonalityAutocomplete(interaction, {
optionName: 'personality',
showVisibility: true,
ownedOnly: false,
});
// â BAD - Duplicating 50+ lines of autocomplete logic
Error Message Patterns
| Layer | Pattern | Example |
|---|---|---|
| api-gateway | Clean JSON, NO emojis | { "error": "NOT_FOUND", "message": "Persona not found" } |
| bot-client | ADD emojis for users | 'â Profile not found.' |
// â
Gateway - clean for programmatic use
sendError(res, ErrorResponses.notFound('Persona'));
// â
Bot - emoji for users
await interaction.editReply({ content: 'â Profile not found.' });
Anti-Patterns from v2 (DON’T DO)
| Pattern | Why Not | v3 Alternative |
|---|---|---|
Generic IRepository<T> |
Too abstract | Concrete service methods |
| DI containers | Over-engineered | Direct instantiation |
ControllerâUseCaseâServiceâRepositoryâORM |
Too many layers | RouteâServiceâPrisma |
| Complex event bus | Unnecessary | Redis pub/sub for cache only |
| Value objects everywhere | Overhead | Simple validation functions |
// â v2 - Container hell
container.bind('PersonalityService').to(PersonalityService);
const service = container.get('PersonalityService');
// â
v3 - Simple
const service = new PersonalityService(prisma);
Dependency Injection
// â
GOOD - Simple constructor injection
class MyService {
constructor(
private prisma: PrismaClient,
private redis: Redis
) {}
}
const service = new MyService(prisma, redis);
When to Extract a Service
Extract when:
- Shared across multiple microservices â common-types
- Complex business logic
- Stateful operations
- Easier testability needed
Keep inline when:
- Used in one place only
- Stateless utility function
- Very simple logic
Complexity Signals (ESLint Warnings)
ESLint warnings indicate when to refactor:
| Warning | Threshold | Action |
|---|---|---|
max-statements |
>30 | Extract helper functions |
complexity |
>15 | Use data-driven patterns |
max-lines-per-function |
>100 | Split responsibilities |
max-params |
>5 | Use options object pattern |
ð See: tzurot-code-quality skill for refactoring patterns
Database Access
Direct Prisma in services – No repository pattern
// â
Direct Prisma
async getPersonality(id: string) {
return this.prisma.personality.findUnique({ where: { id } });
}
// â Generic repository
interface PersonalityRepository {
findById(id: string): Promise<Personality>;
}
Configuration
- Environment variables: Secrets (tokens, DB URLs)
- common-types constants: Application config (timeouts, limits)
import { TIMEOUTS, RETRY_CONFIG } from '@tzurot/common-types';
const timeout = TIMEOUTS.LLM_INVOCATION;
Related Skills
- tzurot-code-quality – Refactoring patterns for complexity
- tzurot-async-flow – BullMQ job patterns
- tzurot-db-vector – Database patterns
- tzurot-types – Type definitions
- tzurot-council-mcp – Major design decisions
References
- Full architecture:
CLAUDE.md#architecture - Project structure:
CLAUDE.md#project-structure - Architecture decisions:
docs/architecture/ARCHITECTURE_DECISIONS.md