Expert assistant for building and maintaining applications using Hexagonal Architecture (Ports and Adapters) with TypeScript, strict module syntax, and strategic planning domain concepts.
Expert assistant for working with hexagonal architecture (Ports and Adapters pattern) in TypeScript projects, with specific expertise in strict TypeScript configurations and strategic planning applications.
This skill helps you work with codebases that follow Hexagonal Architecture principles, particularly those with:
Before making any changes:
1. **Identify the layer structure**:
- `domain/` - Core business logic (framework-agnostic)
- `application/` - Application orchestration (hooks, context)
- `infrastructure/` - External adapters (repositories, data)
- `ui/` - Presentation layer (React components)
2. **Respect dependency rules**:
- Dependencies flow INWARD only
- Domain layer must have ZERO dependencies on outer layers
- Domain depends on: Nothing
- Application depends on: Domain ports (interfaces only)
- Infrastructure depends on: Domain ports (implements interfaces)
- UI depends on: Application hooks and infrastructure
3. **Check the dependency injection setup**:
- Look for `AppContext` or similar DI container
- Understand which dependencies are injected where
- See how hooks access injected dependencies
This is critical for builds to succeed:
**Type-only imports** (interfaces, types):
```typescript
import type { Vision, AnnualGoal } from '../domain/entities';
import type { VisionRepository } from '../domain/ports/repositories';
```
**Value imports** (classes, functions, hooks):
```typescript
import { CPMService } from '../domain/services';
import { InMemoryVisionRepository } from '../infrastructure/repositories';
import { useVision } from '../application/hooks/useVision';
```
**Mixed imports** - separate into two statements:
```typescript
import type { VisionRepository } from '../../domain/ports/repositories';
import type { Vision } from '../../domain/entities';
```
**When to use which**:
Follow these steps:
1. **Start in the domain layer**:
- Define entities in `domain/entities/`
- Create port interfaces in `domain/ports/`
- Implement domain services in `domain/services/`
- Keep it framework-agnostic (no React, no external libs)
2. **Create infrastructure adapters**:
- Implement repository ports in `infrastructure/repositories/`
- Follow the subscription pattern for reactive updates
- Include `subscribe()` method and `notifyListeners()` after mutations
3. **Add application hooks**:
- Create hooks in `application/hooks/`
- Use `useAppContext()` to access injected dependencies
- Subscribe to repository changes via `useEffect`
4. **Wire up dependencies**:
- Instantiate concrete implementations in `ui/App.tsx`
- Add to the `dependencies` object passed to `AppProvider`
- Make available through context
5. **Build UI components**:
- Create components in `ui/components/`
- Use application hooks to access business logic
- Keep components presentational when possible
1. **Identify the layer** you're working in
2. **Check import statements** - ensure type vs value imports are correct
3. **Maintain dependency direction** - never import from outer layers into domain
4. **Update all affected layers** if changing interfaces:
- Port interface in domain
- Implementation in infrastructure
- Hook in application
- Component in UI
When creating or modifying repositories:
```typescript
// 1. Define port interface in domain/ports/repositories/
export interface ExampleRepository {
findById(id: string): Promise<Example | null>;
save(example: Example): Promise<void>;
}
// 2. Implement in infrastructure/repositories/
export class InMemoryExampleRepository implements ExampleRepository {
private data: Map<string, Example> = new Map();
private listeners: (() => void)[] = [];
subscribe(listener: () => void): () => void {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
private notifyListeners(): void {
this.listeners.forEach(listener => listener());
}
async findById(id: string): Promise<Example | null> {
return this.data.get(id) || null;
}
async save(example: Example): Promise<void> {
this.data.set(example.id, example);
this.notifyListeners();
}
}
// 3. Use in hooks with subscription
import { InMemoryExampleRepository } from '../../infrastructure/repositories';
if (exampleRepository instanceof InMemoryExampleRepository) {
const unsubscribe = exampleRepository.subscribe(() => {
// Reload data
});
return () => unsubscribe();
}
```
If working with strategic planning features, understand:
❌ Importing interfaces without `import type`
❌ Using `import type` for classes or functions you need to instantiate
❌ Making domain layer depend on React or external frameworks
❌ Bypassing dependency injection by creating instances in hooks
❌ Breaking the inward-only dependency rule
❌ Forgetting to wire new dependencies through AppContext
```typescript
// 1. domain/entities/Example.ts
export interface Example {
id: string;
name: string;
value: number;
}
// 2. domain/ports/repositories/ExampleRepository.ts
import type { Example } from '../../entities/Example';
export interface ExampleRepository {
findAll(): Promise<Example[]>;
save(example: Example): Promise<void>;
}
// 3. infrastructure/repositories/InMemoryExampleRepository.ts
import type { ExampleRepository } from '../../domain/ports/repositories/ExampleRepository';
import type { Example } from '../../domain/entities/Example';
export class InMemoryExampleRepository implements ExampleRepository {
// ... implementation
}
// 4. application/hooks/useExample.ts
import { useState, useEffect } from 'react';
import type { Example } from '../../domain/entities/Example';
import type { ExampleRepository } from '../../domain/ports/repositories/ExampleRepository';
import { useAppContext } from '../AppContext';
import { InMemoryExampleRepository } from '../../infrastructure/repositories/InMemoryExampleRepository';
export function useExample() {
const { exampleRepository } = useAppContext();
// ... hook implementation
}
```
Before (will fail build):
```typescript
import { Vision, VisionRepository } from '../domain';
```
After (correct):
```typescript
import type { Vision } from '../domain/entities/Vision';
import type { VisionRepository } from '../domain/ports/repositories/VisionRepository';
```
1. NEVER violate the dependency direction (inward only)
2. ALWAYS use correct TypeScript import syntax (type vs value)
3. ALWAYS keep domain layer framework-agnostic
4. ALWAYS use dependency injection via context
5. ALWAYS implement repository subscription pattern for reactive updates
6. NEVER create direct dependencies between UI and infrastructure layers
7. ALWAYS run `pnpm build` after TypeScript changes to verify syntax correctness
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/hexagonal-architecture-planner/raw