-
Notifications
You must be signed in to change notification settings - Fork 1
feat: setup domain core db communication #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
MaximilianAnzinger
merged 29 commits into
main
from
feature/setup-domain-core-db-communication
Dec 22, 2025
Merged
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
531b385
add initial db setup
vtotalova 6871c30
remove redundant fields in db
vtotalova 5e76c47
Merge branch 'main' into feature/db-setup
vtotalova 377fab2
fix CI and code quality issues
vtotalova f114fa8
remove manually migration, update db setup
vtotalova f8c2983
Merge branch 'main' into feature/db-setup
vtotalova 2b86a16
check resolved
vtotalova 515d35f
Merge branch 'feature/db-setup' of github.com:ls1intum/memo into featβ¦
vtotalova 68bf56e
feat: enhance database schema and migration system
vtotalova 5c538fe
style: fix prettier formatting
vtotalova 0d2d8dd
docs: clarify Prisma Studio usage and DATABASE_URL
vtotalova 1b6cb4f
docs: clarify database setup and .env file usage
vtotalova 626561c
remove unnecessary code
vtotalova 9b19b68
fix npm Ci issues
vtotalova 1bffd0d
update doc
vtotalova c7fe36a
fix CI issued
vtotalova a7f4c7f
fix inconsistency between prisma clinet and db setup file
vtotalova abb04f0
Update prisma/migrations/20251203125815_init/migration.sql
vtotalova 791f7f3
Update prisma/schema.prisma
vtotalova 555c4b7
set up domain core
vtotalova b3f6bc6
Merge branch 'feature/db-setup' into feature/setup-domain-core-db-comβ¦
vtotalova 0fefd69
fix code formatting issues
vtotalova 1e768ae
fix: export RelationshipType from domain core and update imports
vtotalova 1df11b0
Merge branch 'main' into feature/setup-domain-core-db-communication
vtotalova ab4d9f2
move prisma build stage upper
vtotalova 3e271ef
Merge branch 'feature/setup-domain-core-db-communication' of github.cβ¦
vtotalova 89eae3f
cosmetic minor changes
vtotalova c0da752
fix test code issues
vtotalova e5ad44e
restructure domain core + code issues fixed
vtotalova File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| # Domain Core Architecture | ||
|
|
||
| This project uses a layered architecture where **only the domain core communicates with the | ||
| database**. | ||
|
|
||
| ## Architecture Flow | ||
|
|
||
| ``` | ||
| Client Side | ||
| β | ||
| Server Actions (app/actions/) | ||
| β | ||
| Service Layer (lib/services/) | ||
| β | ||
| Repository Interface (lib/repositories/dc_interface.ts) | ||
| β | ||
| Repository Implementation (lib/repositories/dc_user_repo.ts) β ONLY layer that touches DB | ||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
| β | ||
| Prisma Client (lib/prisma.ts) | ||
| β | ||
| PostgreSQL Database | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### 1. Domain Layer (`lib/domain/domain_core.ts`) | ||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
|
|
||
| Pure entities and input types. No database dependencies. | ||
|
|
||
| ```typescript | ||
| export interface Competency { | ||
| id: string; | ||
| title: string; | ||
| description: string | null; | ||
| createdAt: Date; | ||
| } | ||
|
|
||
| export interface CreateCompetencyInput { | ||
| title: string; | ||
| description?: string; | ||
| } | ||
| ``` | ||
|
|
||
| ### 2. Repository Interface (`lib/repositories/dc_interface.ts`) | ||
|
|
||
| Contract definition. No implementation. | ||
|
|
||
| ```typescript | ||
| export interface CompetencyRepository { | ||
| create(data: CreateCompetencyInput): Promise<Competency>; | ||
| findById(id: string): Promise<Competency | null>; | ||
| findAll(): Promise<Competency[]>; | ||
| } | ||
| ``` | ||
|
|
||
| ### 3. Repository Implementation (`lib/repositories/dc_user_repo.ts`) | ||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
|
|
||
| Prisma implementation. **ONLY this layer touches the database.** | ||
|
|
||
| ```typescript | ||
| export class PrismaCompetencyRepository implements CompetencyRepository { | ||
| async create(data: CreateCompetencyInput): Promise<Competency> { | ||
| return await prisma.competency.create({ data }); | ||
| } | ||
| } | ||
|
|
||
| export const competencyRepository = new PrismaCompetencyRepository(); | ||
| ``` | ||
|
|
||
| ### 4. Service Layer (`lib/services/`) | ||
|
|
||
| Business logic and validation. | ||
|
|
||
| ```typescript | ||
| export class CompetencyService { | ||
| constructor(private readonly repository: CompetencyRepository) {} | ||
|
|
||
| async createCompetency(data: CreateCompetencyInput) { | ||
| return await this.repository.create(data); | ||
| } | ||
| } | ||
|
|
||
| export const competencyService = new CompetencyService(competencyRepository); | ||
| ``` | ||
|
|
||
| ### 5. Server Actions (`app/actions/`) | ||
|
|
||
| Exposes operations to Client Side. | ||
|
|
||
| ```typescript | ||
| 'use server'; | ||
|
|
||
| import { competencyService } from '@/lib/services/competency-service'; | ||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
|
|
||
| export async function createCompetencyAction(formData: FormData) { | ||
| try { | ||
| const title = formData.get('title') as string; | ||
| const competency = await competencyService.createCompetency({ title }); | ||
| return { success: true, competency }; | ||
| } catch (error) { | ||
| return { success: false, error: error.message }; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Usage from Client Side | ||
|
|
||
| ### Server Components (Default) | ||
|
|
||
| ```tsx | ||
| import { getAllCompetenciesAction } from '@/app/actions/competencies'; | ||
|
|
||
| export default async function CompetenciesPage() { | ||
| const { success, competencies } = await getAllCompetenciesAction(); | ||
|
|
||
| if (!success) return <div>Error loading competencies</div>; | ||
|
|
||
| return ( | ||
| <div> | ||
| {competencies?.map(comp => ( | ||
| <div key={comp.id}> | ||
| <h2>{comp.title}</h2> | ||
| <p>{comp.description}</p> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ### Client Components | ||
|
|
||
| ```tsx | ||
| 'use client'; | ||
|
|
||
| import { createCompetencyAction } from '@/app/actions/competencies'; | ||
| import { useState } from 'react'; | ||
|
|
||
| export default function CreateCompetencyForm() { | ||
| const [message, setMessage] = useState(''); | ||
|
|
||
| async function handleSubmit(formData: FormData) { | ||
| const result = await createCompetencyAction(formData); | ||
| setMessage(result.success ? 'Created!' : result.error); | ||
| } | ||
|
|
||
| return ( | ||
| <form action={handleSubmit}> | ||
| <input name="title" placeholder="Title" required /> | ||
| <textarea name="description" placeholder="Description" /> | ||
| <button type="submit">Create</button> | ||
| {message && <p>{message}</p>} | ||
| </form> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ## Connecting to an API | ||
|
|
||
| To expose data through an API endpoint: | ||
|
|
||
| ```typescript | ||
| // app/api/competencies/route.ts | ||
| import { competencyService } from '@/lib/services/competency-service'; | ||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
| import { NextResponse } from 'next/server'; | ||
|
|
||
| export async function GET() { | ||
| try { | ||
| const competencies = await competencyService.getAllCompetencies(); | ||
| return NextResponse.json({ success: true, competencies }); | ||
| } catch (error) { | ||
| return NextResponse.json({ success: false, error: error.message }, { status: 500 }); | ||
| } | ||
| } | ||
|
|
||
| export async function POST(request: Request) { | ||
| try { | ||
| const body = await request.json(); | ||
| const competency = await competencyService.createCompetency(body); | ||
| return NextResponse.json({ success: true, competency }); | ||
| } catch (error) { | ||
| return NextResponse.json({ success: false, error: error.message }, { status: 500 }); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Rules | ||
|
|
||
| ### β DO | ||
|
|
||
| - Use Server Actions from Client Side | ||
| - Add business logic in Service layer | ||
| - Use repository interfaces in services | ||
| - Call services from API routes for external APIs | ||
|
|
||
| ### β DON'T | ||
|
|
||
| - Never import `prisma` in Client Side/services/API routes | ||
| - Never skip layers (call repository directly from actions/routes) | ||
| - Never put business logic in Server Actions or API routes | ||
| - Never put database queries outside repositories | ||
|
|
||
| ## Existing Entities | ||
|
|
||
| Full CRUD implementations available: | ||
|
|
||
| - **User** - [actions/users.ts](app/actions/users.ts) | ||
| - **Competency** - [actions/competencies.ts](app/actions/competencies.ts) | ||
| - **LearningResource** - [actions/learning-resources.ts](app/actions/learning-resources.ts) | ||
| - **CompetencyRelationship** - | ||
| [actions/competency-relationships.ts](app/actions/competency-relationships.ts) | ||
| - **CompetencyResourceLink** - | ||
| [actions/competency-resource-links.ts](app/actions/competency-resource-links.ts) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| 'use server'; | ||
|
|
||
| import { competencyService } from '@/lib/services/competency'; | ||
|
|
||
| export async function createCompetencyAction(formData: FormData) { | ||
| try { | ||
| const title = formData.get('title') as string; | ||
| const description = formData.get('description') as string | undefined; | ||
|
|
||
|
vtotalova marked this conversation as resolved.
Outdated
|
||
| const competency = await competencyService.createCompetency({ | ||
| title, | ||
| description, | ||
| }); | ||
|
|
||
| return { success: true, competency }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: | ||
| error instanceof Error ? error.message : 'Failed to create competency', | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| export async function getCompetencyAction(id: string) { | ||
| try { | ||
| const competency = await competencyService.getCompetencyById(id); | ||
| return { success: true, competency }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: error instanceof Error ? error.message : 'Competency not found', | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| export async function getAllCompetenciesAction() { | ||
| try { | ||
| const competencies = await competencyService.getAllCompetencies(); | ||
| return { success: true, competencies }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: | ||
| error instanceof Error ? error.message : 'Failed to fetch competencies', | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| export async function updateCompetencyAction( | ||
| id: string, | ||
| title?: string, | ||
| description?: string | ||
| ) { | ||
| try { | ||
| const competency = await competencyService.updateCompetency(id, { | ||
| title, | ||
| description, | ||
| }); | ||
| return { success: true, competency }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: | ||
| error instanceof Error ? error.message : 'Failed to update competency', | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| export async function deleteCompetencyAction(id: string) { | ||
| try { | ||
| await competencyService.deleteCompetency(id); | ||
| return { success: true }; | ||
| } catch (error) { | ||
| return { | ||
| success: false, | ||
| error: | ||
| error instanceof Error ? error.message : 'Failed to delete competency', | ||
| }; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.