[컴] spring에서 @Transactional 이 동작하는지 확인하는 방법

spring transaction / transactional / 스프링 / spring boot

spring에서 @Transactional 이 동작하는지 확인하는 방법

@Transactional 이 제대로 동작하려면

spring 에서 @Transactional 를 사용하려면, public method 에 annotation 을 달아야 한다. 정확히는 이 @Transactional이 붙어있는 method 를 현재 @Bean 말고, 다른 Bean 에서 호출해줘야 한다.

  • [Using @Transactional :: Spring Framework](https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html)
  • [java - Spring @Transactional annotation is not working - Stack Overflow](https://stackoverflow.com/questions/32156652/spring-transactional-annotation-is-not-working)

@Transactional 동작하는지 확인하는 방법

  • TransactionAspectSupport.currentTransactionStatus
    import org.springframework.transaction.interceptor.TransactionAspectSupport;
    ...
    status = TransactionAspectSupport.currentTransactionStatus();
    • Transaction 이 동작하지 않았다면, NoTransactionException 을 던지게 된다.
  • call stack
    • TransactionInterceptor.invoke : call stack 에 TransactionInterceptor.invoke가 보인다.
    • call stack

@Transactional method 를 다른 Bean 에서 호출해야 하는 이유

다른 Bean 에서 호출하지 않고, 현재 @Bean 내의 method 에서 @Transactional method 를 호출하는 것으로는 bean 을 생성하지 않는다. 그러니까 spring 이 처음 run 할 때 @Transactional에 대한 proxy class 를 만드는데, 그 작업을 하지 않는 것 같다.

cglib 를 호출할 때의 call stack:

- SpringNamingPolicy.getClassName(String,String,Object,Predicate) (\spring-core-6.0.7.jar\org.springframework.cglib.core\SpringNamingPolicy.class:41)
- AbstractClassGenerator.generateClassName(Predicate) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:169)
- AbstractClassGenerator.generate(AbstractClassGenerator$ClassLoaderData) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:339)
- Enhancer.generate(AbstractClassGenerator$ClassLoaderData) (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:575)
- AbstractClassGenerator$ClassLoaderData.lambda$new$1(AbstractClassGenerator) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:103)
- 0x0000000800f7bf70.apply(Object) (Unknown Source:-1)
- LoadingCache.lambda$createEntry$1(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:52)
- 0x0000000800f7c5c0.call() (Unknown Source:-1)
- FutureTask.run() (\java.base\java.util.concurrent\FutureTask.class:264)
- LoadingCache.createEntry(Object,Object,Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:57)
- LoadingCache.get(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core.internal\LoadingCache.class:34)
- AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator,boolean) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:126)
- AbstractClassGenerator.create(Object) (\spring-core-6.0.7.jar\org.springframework.cglib.core\AbstractClassGenerator.class:313)
- Enhancer.createHelper() (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:562)
- Enhancer.createClass() (\spring-core-6.0.7.jar\org.springframework.cglib.proxy\Enhancer.class:407)
- ConfigurationClassEnhancer.createClass(Enhancer) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassEnhancer.class:138)
- ConfigurationClassEnhancer.enhance(Class,ClassLoader) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassEnhancer.class:109)
- ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassPostProcessor.class:514)
- ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.annotation\ConfigurationClassPostProcessor.class:304)
- PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(Collection,ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.support\PostProcessorRegistrationDelegate.class:358)
- PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory,List) (\spring-context-6.0.7.jar\org.springframework.context.support\PostProcessorRegistrationDelegate.class:150)
- AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) (\spring-context-6.0.7.jar\org.springframework.context.support\AbstractApplicationContext.class:747)
- AbstractApplicationContext.refresh() (\spring-context-6.0.7.jar\org.springframework.context.support\AbstractApplicationContext.class:565)
- ServletWebServerApplicationContext.refresh() (\spring-boot-3.0.5.jar\org.springframework.boot.web.servlet.context\ServletWebServerApplicationContext.class:146)
- SpringApplication.refresh(ConfigurableApplicationContext) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:732)
- SpringApplication.refreshContext(ConfigurableApplicationContext) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:434)
- SpringApplication.run(String[]) (\spring-boot-3.0.5.jar\org.springframework.boot\SpringApplication.class:310)
- FishApplication.main(String[]) (d:\a\prog\foodpang\purchase\fish\fish\src\main\java\co\foodpang\fish\FishApplication.java:24)
- NativeMethodAccessorImpl.invoke0(Method,Object,Object[])[native method] (\java.base\jdk.internal.reflect\NativeMethodAccessorImpl.class:-1)
- NativeMethodAccessorImpl.invoke(Object,Object[]) (\java.base\jdk.internal.reflect\NativeMethodAccessorImpl.class:77)
- DelegatingMethodAccessorImpl.invoke(Object,Object[]) (\java.base\jdk.internal.reflect\DelegatingMethodAccessorImpl.class:43)
- Method.invoke(Object,Object[]) (\java.base\java.lang.reflect\Method.class:568)
- RestartLauncher.run() (\spring-boot-devtools-3.0.5.jar\org.springframework.boot.devtools.restart\RestartLauncher.class:49)

[컴] JPA 의 entity 로 foreign key 설정하는 법

외래키 / hibernate / join / spring boot / spring / java / orm / 연결 / 관계 설정 / relation entity

JPA 의 entity 로 foreign key 설정하는 법

user table 의 profile_id 가 profile table 의 id 를 참조하도록 하는 예시이다.

아래 code example 이 있다.

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;

@Entity
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "profile_id", referencedColumnName = "id", nullable=false)
    private Profile profile;

    ...
}
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;

import java.time.LocalDateTime;

@Entity
public class Profile {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @OneToOne(mappedBy = "profile")
    private User user;

create table user (id bigint not null auto_increment, profile_id bigint not null, primary key (id)) engine=InnoDB;
create table profile (id bigint not null, primary key (id)) engine=InnoDB;
alter table user add constraint FKl6pykqur53o90qdnhjko0r8ni foreign key (profile_id) references profile (id);

어느 값이 어느값을 가리키는지는 아래 그림을 참고하자.

연결

See Also

  1. Spring Data JPA One To One Relationship Mapping Example
  2. java - When Should I Use @JoinColumn or @JoinTable with JPA? - Stack Overflow

[컴] CQRS pattern

 

CQRS pattern

CQRS (Command Query Responsibility Segregation) 의 핵심은 read/write 가 구분된다는 점이다.

보통 우리가 client 라고 부르는 쪽에서 필요한 데이터를 가져올때(read) 와 변경사항을 저장할 때(write) 우리는 같은 model 을 사용한다.

그런데 CQRS 는 이것을 각자 다른 model 을 사용한다. 한 예는 read때는 read.myserver.com 을 사용하고, write 때는 write.myserver.com 을 사용한다고 봐도 된다.

간략하게 어떤 개념인지를 파악하는데는 마틴파울러의 글이 좋은 듯 싶다. 글에 있는 이미지도 이해를 하는데 도움이 된다.

  • 마틴파울러, CQRS
    • CQRS 의 사용을 주의하라고 한다. 이것은 복잡성을 증가시킬 요인이 크다고 이야기 한다.
    • 시스템의 일부에서만 사용하라고 이야기한다.

CQRS pattern - Azure Architecture Center | Microsoft Learn

CQRS pattern - Azure Architecture Center | Microsoft Learn 의 내용을 일부정리

CQRS 에선 command 는 update 할 때 query 를 read 할 때 쓰인다. 그리고 write 과 read 에 대해서 각자 다른 model들을 사용한다.

  • data 를 update 할 때는 command

  • data 를 read 할 때는 query

  • command 는 task based 여야 한다. data centric 이 아니라.

    • 예를 들어, ’호텔객실 예약’이라고 해야지, ’ReservationStatus 를 Reserved 로 변경’이라고 하지 않는다.
    • 한가지 방법은 command를 보내기 전에,
    • client에서 validation rule들 실행하고,
    • 버튼을 비활성화하고,
    • UI 에 이유를 설명하는 것
    • 예를 들면, ‘방이 남지 않았습니다.’
    • 이 방법으로 server 쪽 command 의 실패의 원인은 race condition 들(2개의 유저들이 마지막 방을 예약하려고 하는것)로 좁혀질 수 있다. 심지어 이런 때도 데이터와 로직(손님을 대기목록에 넣는 것)을 추가해서 해결할 수 있다.
  • command들은 synchronous 하게 처리되지 않고, queue 에 넣어서 asynchronous 하게 처리할 수 있다.

  • Query 들은 db를 변경하지 않는다. query는 도메인 지식을 캡슐화하지 않는 DTO를 반환한다.

이렇게 write 와 read 에 대한 model 을 분리하면, 설계(design), 구현(implementation)이 간단해진다.

isolation 을 강화하기 위해 물리적으로 read용, write용으로 db를 나눠도 된다. read 의 경우 필요한 join 등을 미리 해놓은 결과를 저장해서 보여주는 것처럼 다른 schema 를 사용할 수 있다. 이것은 materialized view 를 쓰거나, NoSQL 같은 다른 type 의 db 를 쓰는 등의 방법을 사용할 수 있다.

이 경우 write 에 사용하는 db 와 read 에 사용하는 db 의 동기화가 필요한데, 이때는 대체로 write db 에 update 가 발생할 때 event 를 발생시켜서 read db 를 update 하게 된다. 다만 이때의 문제는 db에 write 가 된 이후에 event 가 성공적으로 전달되지 못하는 경우 data 의 consistency 를 보장할 수 없다.

read store 와 write store read store 는 write store 의 복제본(read-only replica)이거나, write store 와 상관없이 다른 구조를 갖고 있을 수도 있다. - 여러개의 read-only replica 를 갖고 있으면, 이 replica 가 application instance 에 가까이 있는 경우, query 성능이 향상 될 것이다. - 보통 read store 의 부하가 더 높은데, read 와 write store 를 분리하면, 각각의 부하에 맞게 적절하게 확장이 가능하다.

CQRS 일부 구현은 event sourcing pattern 을 사용한다.

  • application 의 state 가 sequence of events 로 저장된다.
  • 각 event 는 data에 대한 set of changes 를 갖는다.
  • 그래서 이 event 들을 다시 실행하면, 현재의 state 가 되는 것이다.
  • event sourcing pattern 을 사용하면 design 이 복잡해 진다.

구현상 어려운점

  • 복잡성, 개념은 간단한데, application design 이 복잡하다. event sourcing pattern 이 들어가면 더 복잡하다.
  • 메시징, application 은 message 의 실패, duplicated message 에 대한 처리를 해야 한다.
  • Eventual consistency(최종 일관성) : read db 의 data 가 최신이 아닐 수 있다.(stale) 이렇게 stale data 를 기반으로 write 을 요청하는 경우도 있을 수 있는데, 그것을 구분하기 어렵다.

언제 CQRS pattern 을 쓰는가?

  • 많은 사용자가 동일한 데이터에 동시에 액세스 하는
  • 복잡한 프로세스를 안내하는 task-based UI 에서 쓸 수 있다.
  • 쓰기 model 보다 read model 의 성능이 더 중요한 경우, write에 비해 더 많은 read 가 있는 경우등에 유리, write model instance 가 적으면, merge conflict 발생을 최소화할 수 있다.
  • 한팀은 복잡한 domain model 과 write model 에 집중하고, 다른 한팀은 read model 과 UI 에 집중할 수 있는 상황이라면 사용할 수 있다.(me: 세부적인 내용은 다르겠지만, server side, client side 로 나눠서 개발하는 것과 비슷한 경우일듯)
  • 시간이 지남에 따라 진화하고, 여러 다양한 버전의 model 을 포함할 수도 있는 시스템
  • 비지니스 규칙이 정기적으로 변화하하는 시나리오
  • 다른 시스템들과의 통합, 특히 event sourcing 과 결합된 경우.
    • 하나의 sub system 의 일시적인 오류가 다른 sub system 으로 영향이 미치지 않아야 하는 시스템

쓰지 않는게 나은 경우

  • domain 이나 business logic 이 간단한 경우
  • 간단한 CRUD style 이면 충분한 경우

CQRS pattern 을 시스템의 제한된 부분(이 부분이 추후 중요해질 것이라 여겨지는)에 적용하는 것을 고려해보자.

예시들 best practices

[컴] 포토샵으로 변형된 얼굴사진 감별

 

얼굴 포토샵 감지 / 감별 / 확인 방법 / 뽀샵

포토샵으로 변형된 얼굴사진 감별

FAL detector, Face-Aware Liquify detector

Adobe Research와 UC Berkley에서 개발한 AI 기술인 FAL Detector는 포토샵의 Face-Aware Liquify’ 기능을 이용하여 왜곡된 이미지를 학습

Reference

  1. 유명인 얼굴이 어떻게 포토샵 처리되었는지 보여주는 AI 도구 등장 : 클리앙
  2. AI Tool Reveals How Celebrities’ Faces Have Been Photoshopped | PetaPixel

[컴] Remix vs Next.js

리믹스

Remix vs Next.js

Remix 쪽에서 자세히 Next.js 와 자신의 차이를 설명해 준다.

개인적으로 Nextjs 의 복잡함을 좋아하지 않는데, Remix 가 좋은 대안을 주는 듯 하다.

대략 기억나는 것을 정리중…

  • Next.js 보다 빠르다.
  • HTTP stale-while-revalidate caching directive 를 이용한다.
  • Next.js 가 느린 이유중 하나는 SSG(static site generation)을 사용할 수 없는 화면인 경우 Next.js 는 브라우저에서 서버로 검색결과를 조회하고, 그 data 를 보고 이미지를 가져와야 해서이다. 더구나 서버로 검색결과를 조회하는 시점도 브라우저가 최초의 javascript 를 받은 후 처리하고 나서야 시작하게 된다. (network waterfall request chain)
  • Nextjs 가 data 를 가져오는데 4가지 모드를 제공하는데, Remix는 1개의 모드(loader)를 제공.

[컴] 확장성, 유지보수성, Scalability and Maintainability

Scalability and Maintainability

ref. 1 의 Chapter 1, Reliable, Scalable, and Maintainable Applications

확장성 Scalability

성능에 대한 표현

Scalability > Describing Performance

p.14

  • 응답시간의 평균은 유저들이 일반적으로 어떤 평균 응답시간을 경험했는지를 말해주지 못한다.
  • 이때는 percentiles(백분위수)를 사용하는 것이 낫다.
  • 중앙값(median)은 50번째 백분위수라서 p50 으로 불린다.
  • 중앙값이 1.5ms 라고 하면, 절반은 1.5ms 보다 느렸고, 절반은 1.5ms 보다 빨랐다고 알 수 있다.

p. 15

  • 예를 들어 Amazon은 내부 서비스에 대한 응답 시간 요구 사항을 99.9% 라고 설명한다. 이말은 1,000건의 요청 중 1건에만 영향을 미친다는 뜻이다. 이는 요청이 가장 느린 고객이 종종 계정에 가장 많은 데이터를 보유하고, 구매횟수가 많은 고객이기 때문이다. 즉 가장 가치있는 고객들이다.[19].
  • 반면에 99.99번째 백분위수(요청 10,000건 중 가장 느린 1건)를 최적화하는 것은 비용이 너무 많이 들고 아마존의 목적에 비해 충분한 이점을 얻지 못하는 것으로 간주되었다.
  • 응답 시간이 100밀리초 증가하면 매출이 1% 감소하고[20], 1초 느려지면 고객 만족 지표가 16% 감소한다는 보고도 있다.[21, 22]

p. 16

  • 응답 시간 데이터를 집계하는 올바른 방법은 히스토그램을 추가하는 것
  • 시간 해상도를 낮추거나 여러 머신의 데이터를 결합하기 위해 백분위수를 평균하는 것은 수학적으로 의미가 없다.

부하 다루기

Scalability > Approaches for Coping with Load

  • scale up(수직확장, 더 강력한 머신으로 이동)
  • scale out(수평확장, 여러 대의 작은 머신에 부하를 분산)
  • 탄력적 시스템(elastic system)은 부하를 예측할 수 없는 경우에 유용할 수 있다. 하지만 수동으로 확장하는 시스템이 더 간단하고 운영상의 돌발 상황이 적을 수 있다.


  • ’단일 노드로 된 상태 저장 데이터 시스템(stateful data system)’을 분산 설정으로 전환하면 많은 복잡성이 추가될 수 있다.
  • 일반적인 방법은 ‘확장 비용(scaling cost)’이나 ’고가용성 요건(high availability requirements)’ 들이 데이터베이스를 분산하는 것이 나은 시점이 올때까지 단일 노드에 데이터베이스를 유지(스케일업)하는 것.
  • 그러나 요즘 분산시스템을 구성하기가 더 편리해져서 향후, 대량의 데이터나 트래픽을 처리하지 않는 사용사례에서도 분산형 데이터 시스템이 기본이 될 수 있다.


  • 대규모로 운영되는 시스템의 아키텍처는 일반적으로 애플리케이션에 따라 매우 특수하다.
  • 그래서 모든 경우에 사용가능한 확장 가능한 아키텍처(비공식적으로 마법의 확장 소스라고 알려진)와 같은 것은 존재하지 않는다.
  • 특정 응용을 위해 잘 확장되는 구조는 어떤 작업이 일반적이고 어떤 작업이 드문지에 대한 가정, 즉 부하 매개변수(load parameters)를 기반으로 구축된다. 이러한 가정이 잘못된 것으로 판명되면, scaling 을 위한 노력은 가장 최악의 낭비가 되어 버린다.
  • 그래서 초기스타트업이나, 그렇게 흥행이 되지 않은 제품에서는 미래 부하량에 맞춰서 확장하는 것보단 제품 기능들을 빠르게 재시작(iterate)할 수 있는 것이 더 중요하다.

Maintainability

  • 소프트웨어 비용의 대부분은 지속적인 유지 보수에 발생
    • 버그 수정
    • 시스템 운영 유지
    • 장애 조사
    • 새로운 플랫폼에 맞게 조정
    • 새로운 사용 사례에 맞게 수정
    • 기술 부채 상환
    • 새로운 기능 추가
  • 유지보수시 고통을 최소화하도록 sw를 설계, 그래야 legacy sw 가 되지 않는다.
  • 이를 위한 3가지 설계원칙, 이 원칙을 염두에 두고 시스템을 고민해야 한다.
    • 운영성(Operability) : 운영팀이 쉽게 운영할 수 있도록
    • 단순성(Simplicity) : 새로운 엔지니어가 시스템을 쉽게 이해할 수 있도록
    • 진화가능성(Evolvability) : 확장성(extensibility), 수정가능성(modifiability), 가소성(plasticity) 라고도 부른다.

운영성

  • 좋은 운영성‘일상적인 작업(routine task)을 쉽게 만드는 것’ 그래서 운영팀이 고부가가치 활동에 집중할 수 있도록 하는 것을 의미
  • 좋은 운영팀이 중요
  • ’좋은 운영팀’이란
    • 시스템 상태 모니터링 및 불량 상태 발생 시 신속한 서비스 복구
    • 시스템 장애 또는 성능 저하와 같은 문제의 원인을 추적합니다.
    • 보안 패치를 포함한 소프트웨어와 플랫폼을 최신 상태로 유지
    • 서로 다른 시스템이 서로에게 어떤 영향을 미치는지 파악하여 문제가 되는 변경이 손상을 일으키기 전에 피할 수 있도록 유지보수성 확보
    • 향후 문제를 예측하고 문제가 발생하기 전에 해결(예: 용량 계획)
    • 배포, 구성 관리 등을 위한 모범 사례 및 도구 설정
    • 애플리케이션을 한 플랫폼에서 다른 플랫폼으로 이동하는 것과 같은 복잡한 유지 관리 작업 수행
    • 구성이 변경될 때 시스템의 보안 유지
    • 운영을 예측 가능하게 하고 프로덕션 환경을 안정적으로 유지하는 데 도움이 되는 프로세스 정의
    • 개별 직원이 이동하는 경우에도 시스템에 대한 조직의 지식 유지

routine task 를 쉽게 만드는 방법 :

  • 우수한 모니터링을 통해 런타임 동작 및 시스템 내부에 대한 가시성 제공
  • 자동화 및 표준 도구와의 통합을 위한 우수한 지원 제공
  • 개별 머신에 대한 종속성 방지(시스템 전체가 중단 없이 계속 실행되는 동안 유지보수를 위해 머신을 중단할 수 있음)
  • 좋은 문서이해하기 쉬운 운영 모델 제공(“X를 하면 Y가 발생합니다”)
  • 좋은 기본 동작을 제공하되, 관리자가 필요할 때 기본값을 재정의할 수 있는 자유를 제공해야 합니다.
  • 적절한 경우 자가 복구 기능을 제공하되, 필요한 경우 관리자가 시스템 상태를 수동으로 제어할 수 있어야 합니다.
  • 예측 가능한 동작을 보여줌으로써 돌발 상황 최소화

단순성 Simplicity

  • 시스템을 단순하게 만드는 것은 accidential complexity 를 줄이는 것을 뜻하기도 한다.
  • accidentail complexity 는 소프트웨어가 해결하려고 하는 문제에서 나온 복잡성이 아니라, 단순히 구현에서 나온 복잡성이라고 정의
  • 이것을 줄이는 좋은 방법중 하나가 추상화(abstraction) 이다.


  • 복잡하고 이해하기 어려운 시스템은 시스템작업과 연관된 모든 사람의 작업속도를 떨어뜨린다.
  • 복잡하고 이해하기 어려운 시스템은 유지비용을 증가시킨다.
  • 복잡성 –> 유지보수 어려움 –> 예산, 일정의 초과
  • 복잡성의 다양한 증상
    • state space 의 폭발
    • 모듈의 긴밀한 결합(tight coupling)
    • 얽힌 종속성
    • 일관되지 않은 명명 및 용어
    • 성능문제를 해결하기 위한 hacks
    • 다른 곳의 이슈를 해결하기 위한 특수케이스
  • 변경시 버그가 발생할 위험이 더 크다.
    • 개발자가 시스템을 이해하기 어렵고, 이유를 찾기 어렵다.
    • 복잡한 시스템에선 숨겨진 가정, 의도하지 않은 결과, 예기치 않은 상호작용을 더 간과하기 쉽다.

진화가능성, 변경을 쉽게 Evolvability

  • 데이터 시스템을 수정하는 것과 변화하는 요구 사항에 맞게 조정하는 것을 쉽게 하려면 단순성 및 추상화가 중요하다.
  • 단순하고 이해하기 쉬운 시스템이 수정하기 더 쉽다.

See Also

  1. storage and retrieval
  2. 확장성, 유지보수성, Scalability and Maintainability

Reference

  1. Designing Data Intensive Applications


[컴] Domain Driven Design(DDD)

 

Domain Driven Design(DDD)

왜 DDD 를 이야기 하는가?

위의 글, Introduction into Domain-Driven Design (DDD), 에 있는 인용문을 보면 DDD 가 말하고자 하는 것을 알 수 있다.

  • 코드에 현실세계를 반영하는 것. 우리의 시스템을 modeling 하는 더 나은 방법이다.

from : Building Microservices by Sam Newman

Eric Evan’s book DDD helped us understand the importance of representing the real world in our code. and showed us better ways to model our systems.

나의 정리

이해한 바를 정리하면, 우리가 우리의 시스템을 우리의 생각대로 그려나갈 수 있지만, 그 그림이 현실세계와 맞닿아 있을때 더 낫다. 그리고 그렇게 시스템을 디자인 하는 것이 DDD 이다.

See Also 4. 를 참고하면, 구체적으로 이 디자인이 어떤식으로 구현되는지를 알 수 있다.

하지만 See Also 5. 의 이야기처럼, DDD 의 기본개념이 뜻하는 바를 이해하는 것이 중요하다. 무조건 DDD에서 제안하는 pattern 이나 기술규칙을 사용하는 것이 DDD가 아니다.

from : Designing a DDD-oriented microservice - .NET | Microsoft Learn

때때로 이러한 DDD 기술 규칙과 패턴은 DDD 접근 방식(DDD aproaches)을 구현하는 데 있어 가파른 학습 곡선을 가진 장애물로 인식되기도 합니다. 하지만 중요한 부분은 패턴 자체가 아니라 코드를 비즈니스 문제에 맞게 구성하고 동일한 비즈니스 용어(유비쿼터스 언어)를 사용하는 것입니다. 또한 DDD 접근 방식(DDD aproaches)은 중요한 비즈니스 규칙이 있는 복잡한 마이크로서비스를 구현하는 경우에만 적용해야 합니다. CRUD 서비스와 같이 더 단순한 책임은 더 간단한 접근 방식으로 관리할 수 있습니다.

Sometimes these DDD technical rules and patterns are perceived as obstacles that have a steep learning curve for implementing DDD approaches. But the important part is not the patterns themselves, but organizing the code so it is aligned to the business problems, and using the same business terms (ubiquitous language). In addition, DDD approaches should be applied only if you are implementing complex microservices with significant business rules. Simpler responsibilities, like a CRUD service, can be managed with simpler approaches.

See Also

  1. What is Domain Driven Design (DDD)? - Stack Overflow
  2. What is Domain Driven Design? - Stack Overflow
  3. Domain Driven Design for Services Architecture | Thoughtworks
  4. Entities and Value Objects: Diving Deep into Domain-Driven Design
  5. Designing a DDD-oriented microservice - .NET | Microsoft Learn