@stackguide/mcp-server 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/LICENSE +674 -0
- package/README.md +453 -0
- package/data/knowledge/python-django/architecture/architecture-patterns.md +201 -0
- package/data/knowledge/python-django/common-issues/common-issues.md +181 -0
- package/data/knowledge/python-django/patterns/drf-patterns.md +133 -0
- package/data/knowledge/react-node/architecture/node-architecture.md +257 -0
- package/data/knowledge/react-node/common-issues/common-issues.md +262 -0
- package/data/knowledge/react-node/patterns/react-patterns.md +244 -0
- package/data/rules/python-django/best-practices/django-best-practices.md +120 -0
- package/data/rules/python-django/coding-standards/django-standards.md +104 -0
- package/data/rules/python-django/security/security-guidelines.md +146 -0
- package/data/rules/react-node/best-practices/react-best-practices.md +195 -0
- package/data/rules/react-node/coding-standards/node-standards.md +192 -0
- package/data/rules/react-node/coding-standards/react-standards.md +155 -0
- package/data/rules/react-node/security/security-guidelines.md +228 -0
- package/dist/config/persistence.d.ts +15 -0
- package/dist/config/persistence.d.ts.map +1 -0
- package/dist/config/persistence.js +171 -0
- package/dist/config/persistence.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +116 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1799 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/knowledgeProvider.d.ts +10 -0
- package/dist/resources/knowledgeProvider.d.ts.map +1 -0
- package/dist/resources/knowledgeProvider.js +130 -0
- package/dist/resources/knowledgeProvider.js.map +1 -0
- package/dist/resources/rulesProvider.d.ts +10 -0
- package/dist/resources/rulesProvider.d.ts.map +1 -0
- package/dist/resources/rulesProvider.js +135 -0
- package/dist/resources/rulesProvider.js.map +1 -0
- package/dist/services/cursorDirectory.d.ts +55 -0
- package/dist/services/cursorDirectory.d.ts.map +1 -0
- package/dist/services/cursorDirectory.js +367 -0
- package/dist/services/cursorDirectory.js.map +1 -0
- package/dist/services/ruleManager.d.ts +18 -0
- package/dist/services/ruleManager.d.ts.map +1 -0
- package/dist/services/ruleManager.js +382 -0
- package/dist/services/ruleManager.js.map +1 -0
- package/dist/services/webDocumentation.d.ts +41 -0
- package/dist/services/webDocumentation.d.ts.map +1 -0
- package/dist/services/webDocumentation.js +237 -0
- package/dist/services/webDocumentation.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Django Common Issues and Solutions
|
|
2
|
+
|
|
3
|
+
Solutions to frequently encountered problems in Django development.
|
|
4
|
+
|
|
5
|
+
## Database Issues
|
|
6
|
+
|
|
7
|
+
### Migration Conflicts
|
|
8
|
+
|
|
9
|
+
**Problem**: Multiple developers create migrations that conflict.
|
|
10
|
+
|
|
11
|
+
**Solution**:
|
|
12
|
+
```bash
|
|
13
|
+
# Merge migrations
|
|
14
|
+
python manage.py makemigrations --merge
|
|
15
|
+
|
|
16
|
+
# Or squash old migrations
|
|
17
|
+
python manage.py squashmigrations app_name 0001 0010
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Circular Import in Models
|
|
21
|
+
|
|
22
|
+
**Problem**: Two models reference each other causing import errors.
|
|
23
|
+
|
|
24
|
+
**Solution**:
|
|
25
|
+
```python
|
|
26
|
+
# Use string reference for ForeignKey
|
|
27
|
+
class Article(models.Model):
|
|
28
|
+
author = models.ForeignKey(
|
|
29
|
+
"users.User", # String reference
|
|
30
|
+
on_delete=models.CASCADE
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Or use lazy import in methods
|
|
34
|
+
def get_related_articles(self):
|
|
35
|
+
from articles.models import Article
|
|
36
|
+
return Article.objects.filter(author=self.author)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Performance Issues
|
|
40
|
+
|
|
41
|
+
### N+1 Query Problem
|
|
42
|
+
|
|
43
|
+
**Problem**: Loop causes multiple database queries.
|
|
44
|
+
|
|
45
|
+
**Solution**:
|
|
46
|
+
```python
|
|
47
|
+
# Before: N+1 queries
|
|
48
|
+
for article in Article.objects.all():
|
|
49
|
+
print(article.author.name)
|
|
50
|
+
|
|
51
|
+
# After: 1 query with join
|
|
52
|
+
for article in Article.objects.select_related("author"):
|
|
53
|
+
print(article.author.name)
|
|
54
|
+
|
|
55
|
+
# For reverse relations or M2M
|
|
56
|
+
Article.objects.prefetch_related("comments", "tags")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Slow QuerySet Evaluation
|
|
60
|
+
|
|
61
|
+
**Problem**: Large querysets evaluated multiple times.
|
|
62
|
+
|
|
63
|
+
**Solution**:
|
|
64
|
+
```python
|
|
65
|
+
# Cache the queryset result
|
|
66
|
+
articles = list(Article.objects.all()) # Evaluate once
|
|
67
|
+
|
|
68
|
+
# Or use iterator for large datasets
|
|
69
|
+
for article in Article.objects.iterator(chunk_size=1000):
|
|
70
|
+
process(article)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Authentication Issues
|
|
74
|
+
|
|
75
|
+
### Custom User Model Not Working
|
|
76
|
+
|
|
77
|
+
**Problem**: Migrated before creating custom user model.
|
|
78
|
+
|
|
79
|
+
**Solution**:
|
|
80
|
+
```python
|
|
81
|
+
# settings.py - Set this BEFORE first migration
|
|
82
|
+
AUTH_USER_MODEL = "users.User"
|
|
83
|
+
|
|
84
|
+
# users/models.py
|
|
85
|
+
from django.contrib.auth.models import AbstractUser
|
|
86
|
+
|
|
87
|
+
class User(AbstractUser):
|
|
88
|
+
# Custom fields
|
|
89
|
+
phone = models.CharField(max_length=20, blank=True)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Session Not Persisting
|
|
93
|
+
|
|
94
|
+
**Problem**: User logged out unexpectedly.
|
|
95
|
+
|
|
96
|
+
**Solution**:
|
|
97
|
+
```python
|
|
98
|
+
# Check session settings
|
|
99
|
+
SESSION_COOKIE_AGE = 1209600 # 2 weeks
|
|
100
|
+
SESSION_SAVE_EVERY_REQUEST = True
|
|
101
|
+
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Static Files Issues
|
|
105
|
+
|
|
106
|
+
### Static Files Not Loading in Production
|
|
107
|
+
|
|
108
|
+
**Problem**: Static files 404 in production.
|
|
109
|
+
|
|
110
|
+
**Solution**:
|
|
111
|
+
```python
|
|
112
|
+
# settings.py
|
|
113
|
+
STATIC_URL = "/static/"
|
|
114
|
+
STATIC_ROOT = BASE_DIR / "staticfiles"
|
|
115
|
+
|
|
116
|
+
# Run collectstatic
|
|
117
|
+
# python manage.py collectstatic
|
|
118
|
+
|
|
119
|
+
# Use whitenoise for serving
|
|
120
|
+
MIDDLEWARE = [
|
|
121
|
+
"django.middleware.security.SecurityMiddleware",
|
|
122
|
+
"whitenoise.middleware.WhiteNoiseMiddleware",
|
|
123
|
+
# ...
|
|
124
|
+
]
|
|
125
|
+
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Form Issues
|
|
129
|
+
|
|
130
|
+
### Form Not Saving Data
|
|
131
|
+
|
|
132
|
+
**Problem**: ModelForm save() not persisting to database.
|
|
133
|
+
|
|
134
|
+
**Solution**:
|
|
135
|
+
```python
|
|
136
|
+
# Ensure commit=True or call save() on instance
|
|
137
|
+
if form.is_valid():
|
|
138
|
+
instance = form.save(commit=False)
|
|
139
|
+
instance.user = request.user # Add extra data
|
|
140
|
+
instance.save()
|
|
141
|
+
form.save_m2m() # Save many-to-many relationships
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### File Upload Not Working
|
|
145
|
+
|
|
146
|
+
**Problem**: request.FILES is empty.
|
|
147
|
+
|
|
148
|
+
**Solution**:
|
|
149
|
+
```html
|
|
150
|
+
<!-- Form must have enctype -->
|
|
151
|
+
<form method="post" enctype="multipart/form-data">
|
|
152
|
+
{% csrf_token %}
|
|
153
|
+
{{ form }}
|
|
154
|
+
<button type="submit">Upload</button>
|
|
155
|
+
</form>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Celery Issues
|
|
159
|
+
|
|
160
|
+
### Task Not Executing
|
|
161
|
+
|
|
162
|
+
**Problem**: Celery task defined but not running.
|
|
163
|
+
|
|
164
|
+
**Solution**:
|
|
165
|
+
```python
|
|
166
|
+
# Ensure app is loaded
|
|
167
|
+
# config/__init__.py
|
|
168
|
+
from .celery import app as celery_app
|
|
169
|
+
__all__ = ("celery_app",)
|
|
170
|
+
|
|
171
|
+
# Register task correctly
|
|
172
|
+
from celery import shared_task
|
|
173
|
+
|
|
174
|
+
@shared_task
|
|
175
|
+
def send_email_task(email_id):
|
|
176
|
+
# Task logic
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
# Call with .delay() or .apply_async()
|
|
180
|
+
send_email_task.delay(email.id)
|
|
181
|
+
```
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Django REST Framework Patterns
|
|
2
|
+
|
|
3
|
+
Common patterns for building APIs with Django REST Framework.
|
|
4
|
+
|
|
5
|
+
## Serializer Patterns
|
|
6
|
+
|
|
7
|
+
### Nested Serializers
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from rest_framework import serializers
|
|
11
|
+
|
|
12
|
+
class AuthorSerializer(serializers.ModelSerializer):
|
|
13
|
+
class Meta:
|
|
14
|
+
model = Author
|
|
15
|
+
fields = ["id", "name", "email"]
|
|
16
|
+
|
|
17
|
+
class ArticleSerializer(serializers.ModelSerializer):
|
|
18
|
+
author = AuthorSerializer(read_only=True)
|
|
19
|
+
author_id = serializers.PrimaryKeyRelatedField(
|
|
20
|
+
queryset=Author.objects.all(),
|
|
21
|
+
write_only=True,
|
|
22
|
+
source="author"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
class Meta:
|
|
26
|
+
model = Article
|
|
27
|
+
fields = ["id", "title", "content", "author", "author_id"]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Dynamic Serializers
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
class DynamicFieldsSerializer(serializers.ModelSerializer):
|
|
34
|
+
def __init__(self, *args, **kwargs):
|
|
35
|
+
fields = kwargs.pop("fields", None)
|
|
36
|
+
super().__init__(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
if fields is not None:
|
|
39
|
+
allowed = set(fields)
|
|
40
|
+
existing = set(self.fields)
|
|
41
|
+
for field_name in existing - allowed:
|
|
42
|
+
self.fields.pop(field_name)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## ViewSet Patterns
|
|
46
|
+
|
|
47
|
+
### Custom Actions
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from rest_framework import viewsets, status
|
|
51
|
+
from rest_framework.decorators import action
|
|
52
|
+
from rest_framework.response import Response
|
|
53
|
+
|
|
54
|
+
class ArticleViewSet(viewsets.ModelViewSet):
|
|
55
|
+
queryset = Article.objects.all()
|
|
56
|
+
serializer_class = ArticleSerializer
|
|
57
|
+
|
|
58
|
+
@action(detail=True, methods=["post"])
|
|
59
|
+
def publish(self, request, pk=None):
|
|
60
|
+
article = self.get_object()
|
|
61
|
+
article.status = "published"
|
|
62
|
+
article.published_at = timezone.now()
|
|
63
|
+
article.save()
|
|
64
|
+
return Response({"status": "published"})
|
|
65
|
+
|
|
66
|
+
@action(detail=False, methods=["get"])
|
|
67
|
+
def recent(self, request):
|
|
68
|
+
recent = self.queryset.order_by("-created_at")[:5]
|
|
69
|
+
serializer = self.get_serializer(recent, many=True)
|
|
70
|
+
return Response(serializer.data)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Filtering and Pagination
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from rest_framework import filters
|
|
77
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
|
78
|
+
|
|
79
|
+
class ArticleViewSet(viewsets.ModelViewSet):
|
|
80
|
+
queryset = Article.objects.all()
|
|
81
|
+
serializer_class = ArticleSerializer
|
|
82
|
+
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
|
83
|
+
filterset_fields = ["status", "author", "category"]
|
|
84
|
+
search_fields = ["title", "content"]
|
|
85
|
+
ordering_fields = ["created_at", "title"]
|
|
86
|
+
ordering = ["-created_at"]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Authentication and Permissions
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from rest_framework.permissions import IsAuthenticated, IsAdminUser
|
|
93
|
+
|
|
94
|
+
class ArticleViewSet(viewsets.ModelViewSet):
|
|
95
|
+
permission_classes = [IsAuthenticated]
|
|
96
|
+
|
|
97
|
+
def get_permissions(self):
|
|
98
|
+
if self.action in ["create", "update", "destroy"]:
|
|
99
|
+
return [IsAdminUser()]
|
|
100
|
+
return super().get_permissions()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Error Handling
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
from rest_framework.views import exception_handler
|
|
107
|
+
from rest_framework.response import Response
|
|
108
|
+
|
|
109
|
+
def custom_exception_handler(exc, context):
|
|
110
|
+
response = exception_handler(exc, context)
|
|
111
|
+
|
|
112
|
+
if response is not None:
|
|
113
|
+
response.data["status_code"] = response.status_code
|
|
114
|
+
response.data["error_type"] = exc.__class__.__name__
|
|
115
|
+
|
|
116
|
+
return response
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## API Versioning
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
# settings.py
|
|
123
|
+
REST_FRAMEWORK = {
|
|
124
|
+
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
|
|
125
|
+
"DEFAULT_VERSION": "v1",
|
|
126
|
+
"ALLOWED_VERSIONS": ["v1", "v2"],
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# urls.py
|
|
130
|
+
urlpatterns = [
|
|
131
|
+
path("api/<version>/", include("api.urls")),
|
|
132
|
+
]
|
|
133
|
+
```
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Node.js Architecture Patterns
|
|
2
|
+
|
|
3
|
+
Recommended architecture patterns for scalable Node.js applications.
|
|
4
|
+
|
|
5
|
+
## Clean Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────┐
|
|
9
|
+
│ Presentation │
|
|
10
|
+
│ (Controllers, Routes, DTOs) │
|
|
11
|
+
├─────────────────────────────────────┤
|
|
12
|
+
│ Application │
|
|
13
|
+
│ (Use Cases, Services) │
|
|
14
|
+
├─────────────────────────────────────┤
|
|
15
|
+
│ Domain │
|
|
16
|
+
│ (Entities, Business Rules) │
|
|
17
|
+
├─────────────────────────────────────┤
|
|
18
|
+
│ Infrastructure │
|
|
19
|
+
│ (Database, External APIs, etc.) │
|
|
20
|
+
└─────────────────────────────────────┘
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Service Layer Pattern
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// services/UserService.ts
|
|
27
|
+
import { User } from '../entities/User';
|
|
28
|
+
import { IUserRepository } from '../repositories/IUserRepository';
|
|
29
|
+
import { CreateUserDTO, UpdateUserDTO } from '../dtos/UserDTO';
|
|
30
|
+
import { NotFoundError, ValidationError } from '../errors';
|
|
31
|
+
|
|
32
|
+
export class UserService {
|
|
33
|
+
constructor(
|
|
34
|
+
private userRepository: IUserRepository,
|
|
35
|
+
private emailService: EmailService
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
async createUser(dto: CreateUserDTO): Promise<User> {
|
|
39
|
+
// Business validation
|
|
40
|
+
const existing = await this.userRepository.findByEmail(dto.email);
|
|
41
|
+
if (existing) {
|
|
42
|
+
throw new ValidationError('Email already registered');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create entity
|
|
46
|
+
const user = User.create({
|
|
47
|
+
email: dto.email,
|
|
48
|
+
password: await this.hashPassword(dto.password),
|
|
49
|
+
name: dto.name
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Persist
|
|
53
|
+
const saved = await this.userRepository.save(user);
|
|
54
|
+
|
|
55
|
+
// Side effects
|
|
56
|
+
await this.emailService.sendWelcome(saved.email);
|
|
57
|
+
|
|
58
|
+
return saved;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async getById(id: string): Promise<User> {
|
|
62
|
+
const user = await this.userRepository.findById(id);
|
|
63
|
+
if (!user) {
|
|
64
|
+
throw new NotFoundError('User');
|
|
65
|
+
}
|
|
66
|
+
return user;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Repository Pattern
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// repositories/IUserRepository.ts
|
|
75
|
+
export interface IUserRepository {
|
|
76
|
+
findById(id: string): Promise<User | null>;
|
|
77
|
+
findByEmail(email: string): Promise<User | null>;
|
|
78
|
+
findAll(options?: FindOptions): Promise<User[]>;
|
|
79
|
+
save(user: User): Promise<User>;
|
|
80
|
+
update(id: string, data: Partial<User>): Promise<User>;
|
|
81
|
+
delete(id: string): Promise<void>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// repositories/PrismaUserRepository.ts
|
|
85
|
+
import { PrismaClient } from '@prisma/client';
|
|
86
|
+
|
|
87
|
+
export class PrismaUserRepository implements IUserRepository {
|
|
88
|
+
constructor(private prisma: PrismaClient) {}
|
|
89
|
+
|
|
90
|
+
async findById(id: string): Promise<User | null> {
|
|
91
|
+
const data = await this.prisma.user.findUnique({ where: { id } });
|
|
92
|
+
return data ? User.fromPersistence(data) : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async save(user: User): Promise<User> {
|
|
96
|
+
const data = await this.prisma.user.create({
|
|
97
|
+
data: user.toPersistence()
|
|
98
|
+
});
|
|
99
|
+
return User.fromPersistence(data);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Dependency Injection
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// container.ts
|
|
108
|
+
import { Container } from 'inversify';
|
|
109
|
+
|
|
110
|
+
const container = new Container();
|
|
111
|
+
|
|
112
|
+
// Repositories
|
|
113
|
+
container.bind<IUserRepository>('UserRepository')
|
|
114
|
+
.to(PrismaUserRepository)
|
|
115
|
+
.inSingletonScope();
|
|
116
|
+
|
|
117
|
+
// Services
|
|
118
|
+
container.bind<UserService>('UserService')
|
|
119
|
+
.toDynamicValue((context) => {
|
|
120
|
+
const repo = context.container.get<IUserRepository>('UserRepository');
|
|
121
|
+
const email = context.container.get<EmailService>('EmailService');
|
|
122
|
+
return new UserService(repo, email);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export { container };
|
|
126
|
+
|
|
127
|
+
// Usage in controller
|
|
128
|
+
@injectable()
|
|
129
|
+
export class UserController {
|
|
130
|
+
constructor(
|
|
131
|
+
@inject('UserService') private userService: UserService
|
|
132
|
+
) {}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## CQRS (Command Query Responsibility Segregation)
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// commands/CreateUserCommand.ts
|
|
140
|
+
export class CreateUserCommand {
|
|
141
|
+
constructor(
|
|
142
|
+
public readonly email: string,
|
|
143
|
+
public readonly password: string,
|
|
144
|
+
public readonly name: string
|
|
145
|
+
) {}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// handlers/CreateUserHandler.ts
|
|
149
|
+
export class CreateUserHandler {
|
|
150
|
+
constructor(private userRepo: IUserRepository) {}
|
|
151
|
+
|
|
152
|
+
async execute(command: CreateUserCommand): Promise<string> {
|
|
153
|
+
const user = User.create(command);
|
|
154
|
+
await this.userRepo.save(user);
|
|
155
|
+
return user.id;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// queries/GetUserQuery.ts
|
|
160
|
+
export class GetUserQuery {
|
|
161
|
+
constructor(public readonly userId: string) {}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// handlers/GetUserHandler.ts
|
|
165
|
+
export class GetUserHandler {
|
|
166
|
+
constructor(private readRepo: IUserReadRepository) {}
|
|
167
|
+
|
|
168
|
+
async execute(query: GetUserQuery): Promise<UserDTO> {
|
|
169
|
+
return this.readRepo.getUserView(query.userId);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// CommandBus
|
|
174
|
+
export class CommandBus {
|
|
175
|
+
private handlers = new Map<string, any>();
|
|
176
|
+
|
|
177
|
+
register(commandName: string, handler: any) {
|
|
178
|
+
this.handlers.set(commandName, handler);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async execute<T>(command: any): Promise<T> {
|
|
182
|
+
const handler = this.handlers.get(command.constructor.name);
|
|
183
|
+
return handler.execute(command);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Event-Driven Architecture
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// events/UserCreatedEvent.ts
|
|
192
|
+
export class UserCreatedEvent {
|
|
193
|
+
constructor(
|
|
194
|
+
public readonly userId: string,
|
|
195
|
+
public readonly email: string,
|
|
196
|
+
public readonly createdAt: Date
|
|
197
|
+
) {}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// EventBus
|
|
201
|
+
export class EventBus {
|
|
202
|
+
private handlers = new Map<string, Function[]>();
|
|
203
|
+
|
|
204
|
+
subscribe(eventName: string, handler: Function) {
|
|
205
|
+
const existing = this.handlers.get(eventName) || [];
|
|
206
|
+
this.handlers.set(eventName, [...existing, handler]);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
publish(event: any) {
|
|
210
|
+
const handlers = this.handlers.get(event.constructor.name) || [];
|
|
211
|
+
handlers.forEach(handler => handler(event));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Usage
|
|
216
|
+
eventBus.subscribe('UserCreatedEvent', async (event: UserCreatedEvent) => {
|
|
217
|
+
await emailService.sendWelcome(event.email);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
eventBus.subscribe('UserCreatedEvent', async (event: UserCreatedEvent) => {
|
|
221
|
+
await analyticsService.trackSignup(event.userId);
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Middleware Pipeline
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// middleware/Pipeline.ts
|
|
229
|
+
type Middleware<T> = (context: T, next: () => Promise<void>) => Promise<void>;
|
|
230
|
+
|
|
231
|
+
export class Pipeline<T> {
|
|
232
|
+
private middlewares: Middleware<T>[] = [];
|
|
233
|
+
|
|
234
|
+
use(middleware: Middleware<T>): this {
|
|
235
|
+
this.middlewares.push(middleware);
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async execute(context: T): Promise<void> {
|
|
240
|
+
const dispatch = async (index: number): Promise<void> => {
|
|
241
|
+
if (index >= this.middlewares.length) return;
|
|
242
|
+
|
|
243
|
+
const middleware = this.middlewares[index];
|
|
244
|
+
await middleware(context, () => dispatch(index + 1));
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
await dispatch(0);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Usage
|
|
252
|
+
const pipeline = new Pipeline<RequestContext>()
|
|
253
|
+
.use(loggingMiddleware)
|
|
254
|
+
.use(authenticationMiddleware)
|
|
255
|
+
.use(validationMiddleware)
|
|
256
|
+
.use(handlerMiddleware);
|
|
257
|
+
```
|