ํฐ์คํ ๋ฆฌ ๋ทฐ
๐ NestJS + Kubernetes & ๋ง์ดํฌ๋ก์๋น์ค ๋ฐฐํฌ: ์ค์ ๊ฐ์ด๋ - NestJS + DDD๋ก ๋๋ฉ์ธ ์ค์ฌ ๋ฐฑ์๋ ๊ตฌ์ถํ๊ธฐ
octo54 2025. 5. 7. 14:20๐ NestJS + Kubernetes & ๋ง์ดํฌ๋ก์๋น์ค ๋ฐฐํฌ: ์ค์ ๊ฐ์ด๋
23. NestJS + DDD๋ก ๋๋ฉ์ธ ์ค์ฌ ๋ฐฑ์๋ ๊ตฌ์ถํ๊ธฐ
๋๊ท๋ชจ SaaS ํ๋ก์ ํธ๋ฅผ ๊ด๋ฆฌํ ๋, ๋น์ฆ๋์ค ๋ก์ง์ด ์ ์ ๋ณต์กํด์ง๋ฉด์ ์ฝ๋ ๋ณต์ก๋์ ๊ด๋ฆฌ ๋ถ๋ด์ด ์ปค์ง๋๋ค.
์ด๋ **DDD(Domain-Driven Design)**์ ์ ์ฉํ์ฌ ๋๋ฉ์ธ ์ค์ฌ์ผ๋ก ๊ตฌ์กฐํํ๋ฉด ์ฝ๋์ ๊ฐ๋
์ฑ, ์ ์ง๋ณด์์ฑ, ํ์ฅ์ฑ์ด ํฌ๊ฒ ํฅ์๋ฉ๋๋ค.
์ด๋ฒ ๊ธ์์๋ NestJS์ DDD๋ฅผ ๊ฒฐํฉํ์ฌ ๋ชจ๋ํ๋ ๋๋ฉ์ธ ์ค์ฌ ๋ฐฑ์๋ ์ค๊ณ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. ๐ง
โ 1. DDD(Domain-Driven Design)๋?
๋น์ฆ๋์ค ๋๋ฉ์ธ์ ์ค์ฌ์ผ๋ก ์ํํธ์จ์ด๋ฅผ ์ค๊ณํ์ฌ ๋ณต์ก์ฑ์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ๋ก ์ ๋๋ค.
ํต์ฌ ๊ฐ๋ ์ค๋ช
๋๋ฉ์ธ | ํน์ ๋น์ฆ๋์ค ๋งฅ๋ฝ์์ ๋ค๋ฃจ๋ ๋ฌธ์ ์์ญ |
์ ๊ทธ๋ฆฌ๊ฒ์ดํธ | ์ฌ๋ฌ ์ํฐํฐ๋ฅผ ๊ทธ๋ฃนํํ์ฌ ์ผ๊ด์ฑ์ ์ ์งํ๋ ๋จ์ |
๋ฆฌํฌ์งํ ๋ฆฌ | ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ ์์ํํ๊ฑฐ๋ ๋ถ๋ฌ์ค๋ ์ญํ |
์๋น์ค | ๋๋ฉ์ธ ๋ก์ง์ ์บก์ํํ์ฌ ์ ๊ณต |
๊ฐ ๊ฐ์ฒด | ๋ถ๋ณํ๋ฉฐ ๋์ผ์ฑ(identity)๋ณด๋ค ๋ด์ฉ์ ์ค์ ์ ๋ ๊ฐ์ฒด |
โ 2. DDD ๊ธฐ๋ฐ ํ๋ก์ ํธ ๊ตฌ์กฐ
src/
โโโ modules/
โ โโโ user/
โ โ โโโ domain/ # ์ํฐํฐ, ๊ฐ ๊ฐ์ฒด, ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ
โ โ โโโ application/ # ์๋น์ค, ์ ์ฆ์ผ์ด์ค
โ โ โโโ infrastructure/ # DB, ์ธ๋ถ API ์ฐ๋
โ โ โโโ presentation/ # ์ปจํธ๋กค๋ฌ, DTO
โโโ shared/ # ๊ณตํต ๋ชจ๋, ์ ํธ๋ฆฌํฐ
โโโ config/
โโโ main.ts
โ 3. ๋๋ฉ์ธ ๋ชจ๋ ๊ตฌ์ฑ ์์ (User)
๐ฆ 1) ๋๋ฉ์ธ ์ํฐํฐ
๐ domain/user.entity.ts
export class User {
constructor(
public readonly id: string,
private _name: string,
private _email: string,
) {}
get name(): string {
return this._name;
}
changeName(newName: string) {
if (!newName) throw new Error('์ด๋ฆ์ด ์ ํจํ์ง ์์ต๋๋ค.');
this._name = newName;
}
}
๐ฆ 2) ๊ฐ ๊ฐ์ฒด
๐ domain/email.vo.ts
export class Email {
constructor(private readonly value: string) {
if (!this.isValid(value)) throw new Error('์ ํจํ์ง ์์ ์ด๋ฉ์ผ');
}
private isValid(email: string): boolean {
return /\S+@\S+\.\S+/.test(email);
}
get email(): string {
return this.value;
}
}
๐ฆ 3) ๋ฆฌํฌ์งํ ๋ฆฌ ์ธํฐํ์ด์ค
๐ application/user.repository.ts
export interface UserRepository {
save(user: User): Promise<void>;
findById(id: string): Promise<User | null>;
}
๐ฆ 4) ์๋น์ค ๊ณ์ธต
๐ application/user.service.ts
@Injectable()
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
async createUser(name: string, email: string): Promise<User> {
const user = new User(uuid(), name, new Email(email).email);
await this.userRepository.save(user);
return user;
}
async changeUserName(id: string, newName: string): Promise<void> {
const user = await this.userRepository.findById(id);
if (!user) throw new NotFoundException('User not found');
user.changeName(newName);
await this.userRepository.save(user);
}
}
๐ฆ 5) ์ธํ๋ผ ๊ณ์ธต (TypeORM ๊ธฐ๋ฐ)
๐ infrastructure/typeorm-user.repository.ts
@Injectable()
export class TypeOrmUserRepository implements UserRepository {
constructor(@InjectRepository(User) private repo: Repository<User>) {}
async save(user: User): Promise<void> {
await this.repo.save(user);
}
async findById(id: string): Promise<User | null> {
return this.repo.findOne({ where: { id } });
}
}
๐ฆ 6) ํ๋ ์ ํ ์ด์ ๊ณ์ธต (์ปจํธ๋กค๋ฌ)
๐ presentation/user.controller.ts
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
async create(@Body() dto: CreateUserDto) {
return this.userService.createUser(dto.name, dto.email);
}
@Patch(':id/name')
async updateName(@Param('id') id: string, @Body() dto: UpdateNameDto) {
await this.userService.changeUserName(id, dto.newName);
return { message: 'Name updated' };
}
}
โ 4. ๋ชจ๋ ๋จ์ DI ๊ตฌ์ฑ
๐ user.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [
UserService,
{ provide: 'UserRepository', useClass: TypeOrmUserRepository },
],
controllers: [UserController],
})
export class UserModule {}
โ 5. DDD๋ฅผ ํ์ฉํ ์ ์ง๋ณด์ ์ ๋ต
๋ฌธ์ ์ DDD๋ก ํด๊ฒฐ ๋ฐฉ๋ฒ
๋น์ฆ๋์ค ๋ก์ง์ด ๋ณต์ก | ๋๋ฉ์ธ ์ค์ฌ ๋ชจ๋๋ก ๋ก์ง ์บก์ํ |
๋น๋๊ธฐ ์ด๋ฒคํธ ๊ด๋ฆฌ | Event Handler๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ ๋ถ๋ฆฌ |
๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๊ด๋ฆฌ | ๊ฐ ๊ฐ์ฒด(VO)๋ก ๊ธฐ๋ณธ ๊ฒ์ฆ ์ฒ๋ฆฌ |
์๋น์ค ๊ฐ ์ค๋ณต ๋ก์ง | ๊ณตํต ๋ชจ๋์ shared ๋๋ ํ ๋ฆฌ๋ก ๋ถ๋ฆฌ |
โ 6. DDD๋ฅผ ์ ์ฉํ ๋ ์ฃผ์์
- ๋๋ฉ์ธ ์ง์์ ์ ๋๋ก ๋ฐ์ํด์ผ ํจ (๊ธฐํ์์ ํ์ ํ์)
- ๋ณต์ก๋๋ฅผ ์ค์ด๊ธฐ ์ํด ํ์ํ ๊ณณ์๋ง ์ ์ฉ (๋ชจ๋ ๋ชจ๋์ ๊ฐ์ ์ ์ฉ X)
- ๊ธฐ๋ณธ CRUD ๊ฐ์ ๋จ์ ๋ก์ง์ DDD๋ฅผ ๊ตณ์ด ์ฌ์ฉํ์ง ์์๋ ๋จ
โ ๊ฒฐ๋ก : NestJS์ DDD๋ก ๋ณต์กํ ๋๋ฉ์ธ ๊ตฌ์กฐํ
โ
๋๋ฉ์ธ ์ค์ฌ์ผ๋ก ์ค๊ณํ์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๊น๋ํ๊ฒ ์ ์ง
โ
๋๋ฉ์ธ ๋ก์ง, ์ธํ๋ผ, ํ๋ ์ ํ
์ด์
๊ณ์ธต์ ๋ช
ํํ ๊ตฌ๋ถ
โ
์ฝ๋ ๋ณต์ก๋ ๊ฐ์ ๋ฐ ์ ์ง๋ณด์์ฑ ํฅ์
โ
๋๊ท๋ชจ SaaS ๋๋ ๋ณต์กํ ์๋น์ค์ ๋งค์ฐ ์ ํฉ
๋ค์ ๊ธ์์๋ NestJS ํ๋ก์ ํธ์์ CI/CD ์๋ํ ํ์ดํ๋ผ์ธ์ ArgoCD์ GitHub Actions๋ก ๊ตฌ์ถํ๋ ์ค์ ๊ฐ์ด๋๋ฅผ ์๊ฐํฉ๋๋ค! ๐
๐ ๋ค์ ๊ธ ์๊ณ : NestJS CI/CD ์๋ํ – ArgoCD + GitHub Actions
๐ ๋ค์ ํธ: 24. NestJS CI/CD ์๋ํ๋ก ์ด์ ํจ์จ ๊ทน๋ํ
NestJS DDD,NestJS ๋๋ฉ์ธ ์ค์ฌ ์ค๊ณ,NestJS ๋๋ฉ์ธ ๋ชจ๋,NestJS CQRS DDD,NestJS ๋ชจ๋ํ,NestJS ๋ณต์ก์ฑ ๊ด๋ฆฌ,NestJS ๋๊ท๋ชจ ํ๋ก์ ํธ,NestJS ๋๋ฉ์ธ ๋ชจ๋ธ๋ง,NestJS ์ ๊ทธ๋ฆฌ๊ฒ์ดํธ,NestJS ๊ฐ ๊ฐ์ฒด ์ค๊ณ
'study > ๋ฐฑ์๋' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
- Total
- Today
- Yesterday
- Docker
- seo ์ต์ ํ 10๊ฐ
- Next.js
- llm
- gatsbyjs
- NestJS
- Python
- Prisma
- LangChain
- Ktor
- kotlin
- SEO์ต์ ํ
- CI/CD
- ๊ฐ๋ฐ๋ธ๋ก๊ทธ
- Webpack
- App Router
- ํ๋ก ํธ์๋๋ฉด์
- nodejs
- ํ๋ก ํธ์๋
- nextJS
- ๊ด๋ฆฌ์
- ๋ฐฑ์๋๊ฐ๋ฐ
- github
- AI์ฑ๋ด
- ์น๊ฐ๋ฐ
- ํ์ด์ฌ ์๊ณ ๋ฆฌ์ฆ
- rag
- PostgreSQL
- REACT
- fastapi
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |