+21
-8
docs/unauthed-use-cases.md
+21
-8
docs/unauthed-use-cases.md
···
9
9
## Why This Pattern Makes Sense
10
10
11
11
### Business Reality
12
+
12
13
- Many operations serve both user types (e.g., viewing public collections)
13
14
- Authenticated users often get enhanced functionality or additional data
14
15
- Same core business logic with context-dependent variations
15
16
16
17
### Architectural Benefits
18
+
17
19
- **Single Responsibility**: One use case per business operation
18
20
- **Explicit Dependencies**: Clear what context is needed
19
21
- **Clean Layer Separation**: Each layer handles its concerns appropriately
···
22
24
## Implementation Pattern
23
25
24
26
### Controller Layer
27
+
25
28
Extract authentication context from HTTP layer and pass it down:
26
29
27
30
```typescript
···
34
37
```
35
38
36
39
### Use Case Layer
40
+
37
41
Accept optional caller context and pass it to domain services:
38
42
39
43
```typescript
···
53
57
```
54
58
55
59
### Domain Service Layer
60
+
56
61
Make decisions based on caller identity:
57
62
58
63
```typescript
···
71
76
72
77
```typescript
73
78
export class GetCollectionPageUseCase {
74
-
async execute(query: GetCollectionPageQuery): Promise<Result<GetCollectionPageResult>> {
79
+
async execute(
80
+
query: GetCollectionPageQuery,
81
+
): Promise<Result<GetCollectionPageResult>> {
75
82
// Core business logic remains the same
76
83
const collection = await this.getCollection(query.collectionId);
77
-
84
+
78
85
// Context affects behavior where needed
79
86
const profile = await this.profileService.getProfile(
80
87
collection.authorId.value,
81
-
query.callerDid // undefined for unauth users
88
+
query.callerDid, // undefined for unauth users
82
89
);
83
-
90
+
84
91
// Business rules can vary based on context
85
-
if (this.isPrivateCollection(collection) && !this.canAccess(collection, query.callerDid)) {
92
+
if (
93
+
this.isPrivateCollection(collection) &&
94
+
!this.canAccess(collection, query.callerDid)
95
+
) {
86
96
return err(new UnauthorizedError());
87
97
}
88
-
98
+
89
99
return ok(result);
90
100
}
91
101
}
···
94
104
## Common Use Cases
95
105
96
106
This pattern works well for:
107
+
97
108
- **Profile viewing** (public vs private details)
98
109
- **Content feeds** (personalized vs generic)
99
110
- **Collection browsing** (access control based on caller)
···
102
113
## When to Split Use Cases
103
114
104
115
Consider separate use cases when:
116
+
105
117
- **Fundamentally different operations**: `LoginUseCase` vs `GetPublicDataUseCase`
106
118
- **Complex branching**: If auth/unauth paths are completely different
107
119
- **Security isolation**: When you want to ensure certain code paths never run for unauthenticated users
···
120
132
121
133
```typescript
122
134
// In routes
123
-
router.get('/:id',
135
+
router.get(
136
+
'/:id',
124
137
authMiddleware.optionalAuthentication(), // Sets req.did if token present
125
-
(req, res) => controller.execute(req, res)
138
+
(req, res) => controller.execute(req, res),
126
139
);
127
140
```
128
141
+2
-2
src/modules/cards/infrastructure/http/controllers/GetUrlCardViewController.ts
+2
-2
src/modules/cards/infrastructure/http/controllers/GetUrlCardViewController.ts