Step 1 — Input: Create Lists & Add Knowledge
This step establishes the full architectural foundation — every layer from Slack UI down to database persistence. Users can create themed lists, add knowledge items through the App Home, and save any Slack message as knowledge via message actions.
User Stories
Story 1.1 — Create a List
As a user, I can create a thematic list via Slack so I can organize knowledge items.
Flow:
App Home → Click "Lijst toevoegen" → Fill modal (Name, Description, Icon) → Submit
→ Backend persists Category entity → Appears in App Home listStory 1.2 & 1.3 — Add Knowledge (Shared Modal)
Both of the following stories open the same AddKnowledge modal and hit the same POST /api/knowledge endpoint. The only difference is how the modal is opened and what gets pre-filled.
The AddKnowledge Modal
| Field | Type | Required | Notes |
|---|---|---|---|
| Title | Text input | ✅ | Short title for the knowledge item |
| Content | Textarea | ❌ | Free-form text, notes, or context |
| Category | Dropdown | ✅ | Select from existing lists |
| Tags | Text input | ❌ | Comma-separated tags |
| URL | URL input | ❌ | Optional link. In Step 1, stored as-is. In Step 2, triggers content extraction. |
Story 1.2 — Add Knowledge via App Home
As a user, I can add a knowledge item from the App Home.
App Home → Click "Kennis toevoegen" → AddKnowledge modal opens (empty) → Fill fields → SubmitAll fields start empty. The user fills in everything manually.
Story 1.3 — Save a Message via Message Action (⋮)
As a user, I can save any Slack message as a knowledge item using the three-dot menu.
Hover over message → Click ⋮ → "More actions" → "Opslaan in Knowledge Hub"
→ AddKnowledge modal opens pre-filled → User completes remaining fields → SubmitPre-fill logic:
| Field | Pre-filled with | Example |
|---|---|---|
| Content | Full message text | "Kijk deze resource over accessibility: https://web.dev/learn" |
| URL | First URL found in the message (regex) | https://web.dev/learn |
| Title | Empty — user must fill in | — |
| Category | Empty — user must select | — |
TIP
Both stories share the same modal, the same Slack service call, and the same backend endpoint. Story 1.3 simply pre-fills the modal using data from the Slack message context (body.message.text).
What Happens to the URL in Step 1?
In Step 1, the URL is stored as a plain string on the Knowledge entity. Nothing is fetched or extracted — that's Step 2's job.
| Step | URL Behavior |
|---|---|
| Step 1 only | URL saved in the url field. content = whatever the user typed. urlMetadata = null. |
| After Step 2 | URL saved in url field. Backend fetches the page → extracts text → appends to content. urlMetadata = extraction details. |
Architecture Flow
The create knowledge flow demonstrates the full Orchestrator Pipeline from the 10-Layer Architecture:
Backend Files (~40 files including dependencies)
Constants (13 files)
Required by entities, DTOs, and services for validation, limits, and field names:
Entity/EntityLimits.php- Max lengths for fieldsErrorMessages.php- Validation error messagesValidation/ValidationConstants.php- Validation constraintsValidation/ValidationMessages.php- Validation error messagesGedmo/GedmoConstants.php- Timestampable constantsEntity/EntityFields.php- Entity field namesEntity/OrmConstants.php- ORM relationship constantsEntity/RequestKeys.php- Request payload keysNotion/NotionConstants.php- Notion-related constantsDtoKeys.php- DTO property keysResponse/ResponseKeys.php- Response keysLogMessages.php- Logging messagesMapper/MapperMethods.php- Mapper method names
Enums (2 files)
Enum/Status.php- Base status enumEnum/KnowledgeStatus.php- Knowledge-specific statuses
Base Infrastructure — The Foundation (5 files)
These 5 classes are shipped at full size — they are the base that every service in the MVP will extend.
| File | Purpose | Extends | ~LOC |
|---|---|---|---|
Service/BaseService.php | Logger, EntityManager, flush pattern | — | 60 |
Service/BaseDomainService.php | Domain-specific logging, validation failure logging | BaseService | 100 |
Service/BaseOrchestrator.php | executeWithLogging(), operation start/success/failure logging | OrchestratorInterface | 155 |
Mapper/BaseMapper.php | toDto(), toEntity(), toDtoCollection() template methods | MapperInterface | 80 |
Service/BaseQueryService.php | Read-only query base with logging, no persist/flush | BaseService | 60 |
Entities (2 files)
| File | Key Properties | Relationships | ~LOC |
|---|---|---|---|
Entity/Category.php | id, name, description, icon, notionId, timestamps, isActive | knowledges (OneToMany → Knowledge) | 130 |
Entity/Knowledge.php | id, title, content, tags (JSON), userId, status, url, urlMetadata (JSON), notionId, timestamps | category (ManyToOne → Category) | 200 |
IMPORTANT
Excluded from entities: targetGroups (ManyToMany), subscriptions, summaries, sourceMessage, attachments. These are MVP concerns.
Repositories (2 files)
| File | Inherited Methods | Custom Methods | ~LOC |
|---|---|---|---|
Repository/CategoryRepository.php | find(), findAll(), findOneBy() | — | 25 |
Repository/KnowledgeRepository.php | find(), findAll(), findBy() | findByCategoryAndDateRange() (used in Step 3) | 50 |
Mappers (2 files)
| File | toDto() Maps | toEntity() Maps | ~LOC |
|---|---|---|---|
Mapper/CategoryMapper.php | id, name, description, icon, notionId, createdAt, isActive | name, description, icon | 60 |
Mapper/KnowledgeMapper.php | id, title, content, category, tags, userId, status, url, createdAt | title, content, categoryId, tags, userId, url | 80 |
Services (5 files)
| File | Layer | Methods | ~LOC |
|---|---|---|---|
Service/Category/CategoryPersistenceService.php | Persistence | create(Category): Category | 40 |
Service/Knowledge/KnowledgePersistenceService.php | Persistence | create(Knowledge): Knowledge | 40 |
Service/Category/CategoryOrchestrator.php | Orchestration | create(), list() | 100 |
Service/Knowledge/KnowledgeOrchestrator.php | Orchestration | create(), list() | 130 |
Service/EmojiConverter.php | Helper | Emoji conversion | 50 |
Controllers (2 files)
| File | Routes | ~LOC |
|---|---|---|
Controller/CategoryController.php | POST /api/categories (create), GET /api/categories (list) | 60 |
Controller/KnowledgeController.php | POST /api/knowledge (create), GET /api/knowledge (list) | 70 |
WARNING
Only these 4 routes exist. No show, update, delete, or sync endpoints.
Request DTOs (2 files)
| File | Purpose | ~LOC |
|---|---|---|
DTO/Request/Category/CreateCategoryRequest.php | Category creation validation | 30 |
DTO/Request/Knowledge/CreateKnowledgeRequest.php | Knowledge creation validation | 40 |
Response DTOs (5 files)
| File | Purpose | ~LOC |
|---|---|---|
DTO/Response/BaseResult.php | Base response wrapper | 20 |
DTO/Response/Category/CategoryListResponse.php | Category list response | 30 |
DTO/Response/Category/CategoryResponse.php | Single category response | 40 |
DTO/Response/Knowledge/KnowledgeListResponse.php | Knowledge list response | 30 |
DTO/Response/Knowledge/KnowledgeResponse.php | Single knowledge response | 50 |
Slack Files (Separate POC)
IMPORTANT
Slack/TypeScript files are in a SEPARATE POC. This document covers only the Symfony/PHP backend. See the Slack POC for frontend implementation details.
The Slack app implements the same user stories with these components:
- Views (4): dynamicHomeTab, categoriesView, addList modal, addKnowledge modal
- Handlers (1): index.ts (view submissions, actions, message shortcuts)
- Services (2): categories.ts, knowledge.ts
- App Entry (1): app.ts (event handlers, health check)
These are NOT included in this Symfony POC scope.
Views
| File | What It Renders | ~LOC |
|---|---|---|
views/home/dynamicHomeTab.ts | App Home with Quick Actions bar, statistics, categories list. Categories view only — no knowledge items view, no AI summaries view | 100 |
views/home/categoriesView.ts | List of categories with icon, name, description, item count. Overflow menu: "Kennis toevoegen" | 80 |
views/modals/categories/addList.ts | Modal: Name (required), Description (optional), Icon (optional). No target groups | 60 |
views/modals/knowledge/addKnowledge.ts | Shared modal for Story 1.2 and 1.3. Accepts optional prefill parameter to populate fields from message action context. Fields: Title, Content, Category select, Tags, URL. No target groups, no AI checkbox | 50 |
Handlers
| File | Handles | ~LOC |
|---|---|---|
handlers/index.ts | View submissions: ADD_LIST → addCategory(), ADD_KNOWLEDGE → addKnowledge(). Actions: home buttons → open modals, category overflow → pre-select category. Message shortcut: save_to_knowledge_hub → extract message text + URL → open AddKnowledge modal with pre-fill | 220 |
Services
| File | API Calls | ~LOC |
|---|---|---|
services/categories.ts | listCategories() — GET, addCategory() — POST | 40 |
services/knowledge.ts | addKnowledge() — POST with title, content, categoryId, tags, url, userId | 40 |
App Entry
| File | Registers | ~LOC |
|---|---|---|
app.ts | app_home_opened, app.view(/.*/), app.action(/.*/), app.shortcut() (message action). Express health check. Category pre-cache on startup. No slash commands, no global shortcuts | 90 |
Verification Checklist
Story 1.1 — Create a List
- [ ]
GET /health→ 200 OK - [ ] Open App Home → welcome header + quick action buttons + empty list
- [ ] Click "Lijst toevoegen" → modal opens → fill name → submit → list appears in App Home
Story 1.2 — Add Knowledge via App Home
- [ ] Click "Kennis toevoegen" → modal opens empty → fill fields → select category → submit → item saved
- [ ]
GET /api/knowledge→ returns created knowledge item with category link
Story 1.3 — Save Message via Action
- [ ] Hover over a Slack message → ⋮ → "Opslaan in Knowledge Hub" → AddKnowledge modal opens
- [ ] Modal
contentfield is pre-filled with the message text - [ ] If message contains a URL →
urlfield is pre-filled with that URL - [ ] Fill in Title and Category → submit → item saved
- [ ] Verify saved item has user's message as content
General
- [ ]
GET /api/categories→ returns created categories as JSON - [ ] Add multiple items → App Home stats update (item count, list count)
Step 1 LOC Summary (Symfony Backend Only)
| Layer | Files | ~LOC |
|---|---|---|
| Constants | 13 | 200 |
| Enums | 2 | 30 |
| Base infrastructure | 5 | 455 |
| Entities | 2 | 330 |
| Repositories | 2 | 75 |
| Mappers | 2 | 140 |
| Services | 5 | 360 |
| Controllers | 2 | 130 |
| Request DTOs | 2 | 70 |
| Response DTOs | 5 | 170 |
| Total | 40 | ~1,960 |
NOTE
| Slack/TypeScript files are in a separate POC (~790 LOC, 11 files) |