지금 당장
(유사) DDD 시작하기
StyleShare 서대원
자바 / C#
파이썬은 왜
파이썬은 왜
파이썬은 왜
파이썬은 왜
예:
광고 노출 목표량
분배 자동화
아무튼 복잡함
DDD? 왜?
https://en.wikipedia.org/wiki/Domain-driven_design
Domain-driven design (DDD)
is an approach to software development
for complex needs
by connecting the implementation
to an evolving model.
https://en.wikipedia.org/wiki/Domain-driven_design
일단 이 책을 질렀습니다
START 라며
그래도 일단 시작해 보자
Pseudo-DDD
Pseudo-DDD
이번 프로젝트를 만들면서
이번 프로젝트를 만들면서
부분적으로 경험했던 과정
이번 프로젝트를 만들면서
부분적으로 경험했던 과정
의사결정을 위해 했던 고민
계층 구조
도메인 주도 설계: 에릭 에반스 (위키북스)
도메인 주도 설계: 에릭 에반스 (위키북스)
도메인 주도 설계: 에릭 에반스 (위키북스)
도메인 계층
도메인 주도 설계: 에릭 에반스 (위키북스)
도메인?
도메인 계층 > 도메인
https://en.wikipedia.org/wiki/Domain-driven_design
소프트웨어로 해결해야 할
문제 영역
도메인 계층 > 도메인
예:
쇼핑몰, SNS,
항공권 예약...
도메인 계층 > 도메인
도메인 계층 > 도메인
그럼 "도메인 주도 개발"은?
도메인 계층 > 도메인
도메인 == 소프트웨어
도메인 계층 > 도메인
도메인 == 소프트웨어
최대한
도메인 계층 > 도메인
도메인 모델
도메인 계층 > 도메인 > 도메인 모델
구성 요소 정의, 요소간의 관계 등등
도메인을 이해하기 위한
개념 모델
도메인 계층 > 도메인 > 도메인 모델
캠페인
도메인 계층 > 도메인 > 도메인 모델
캠페인
총 목표량
집행 기간
도메인 계층 > 도메인 > 도메인 모델
캠페인
목 표 량별일
총 목표량
집행 기간
도메인 계층 > 도메인 > 도메인 모델
광 고 1
광 고 2
캠페인
목 표 량별일
총 목표량
집행 기간
도메인 계층 > 도메인 > 도메인 모델
도메인 계층 > 도메인 > 도메인 모델
도메인 계층 > 도메인 > 도메인 모델
유비쿼터스 언어
도메인 계층 > 도메인 > 도메인 모델 > 유비쿼터스 언어
Ubiquitous Language is the term
Eric Evans uses in Domain Driven Design
for the practice of building up
a common, rigorous language
between developers and users
https://martinfowler.com/bliki/UbiquitousLanguage.html
도메인 계층 > 도메인 > 도메인 모델 > 유비쿼터스 언어
도메인 계층 > 도메인 > 도메인 모델 > 유비쿼터스 언어
도메인 주도 설계: 에릭 에반스 (위키북스)
도메인 주도 설계: 에릭 에반스 (위키북스)
엔티티
도메인 계층 > 엔티티
도메인 모델 구현
도메인 계층 > 엔티티
고유한 식별자를 가진
객체
도메인 계층 > 엔티티
도메인 계층 > 엔티티
도메인 계층 > 엔티티
꼭 AUTO INCREMENT는
아닙니다
도메인 계층 > 엔티티
지속성 무시
도메인 계층 > 엔티티 > 지속성 무시
지속성 구현을 위한
구체적인 정보 포함 X
https://deviq.com/persistence-ignorance
도메인 계층 > 엔티티 > 지속성 무시
지속성 구현 ==
데이터 스토어에 조회/저장
https://deviq.com/persistence-ignorance
도메인 계층 > 엔티티 > 지속성 무시
예:
도메인 계층 > 엔티티 > 지속성 무시
예:
SQLAlchemy Base
도메인 계층 > 엔티티 > 지속성 무시
예:
Django Model
SQLAlchemy Base
도메인 계층 > 엔티티 > 지속성 무시
잉? 지금까지 모델은
다 ORM으로 만들었는데?
도메인 계층 > 엔티티 > 지속성 무시
엔티티는 POPO
https://martinfowler.com/bliki/POJO.html
도메인 계층 > 엔티티 > 지속성 무시
엔티티는 POPO
https://martinfowler.com/bliki/POJO.html
도메인 모델을 코드로
구현하는 것이 목표
도메인 계층 > 엔티티 > 지속성 무시
엔티티 지속성 모델
도메인 계층 > 엔티티 > 지속성 무시
엔티티 지속성 모델Mapper
도메인 계층 > 엔티티 > 지속성 무시
그래서 캠페인이나 광고도
POPO로 만들었냐구요?
도메인 계층 > 엔티티 > 지속성 무시
아뇨 ㅎㅎ
도메인 계층 > 엔티티 > 지속성 무시
아뇨 ㅎㅎ
도메인 계층 > 엔티티 > 지속성 무시
아뇨 ㅎㅎ
도메인 계층 > 엔티티 > 지속성 무시
아뇨 ㅎㅎ
도메인 계층 > 엔티티 > 지속성 무시
아뇨 ㅎㅎ
도메인 계층 > 엔티티 > 지속성 무시
변명
도메인 계층 > 엔티티 > 지속성 무시
https://stackoverflow.com/questions/14024912/ddd-persistence-model-and-domain-model
도메인 계층 > 엔티티 > 지속성 무시
https://stackoverflow.com/questions/14024912/ddd-persistence-model-and-domain-model
도메인 계층 > 엔티티 > 지속성 무시
https://stackoverflow.com/questions/14024912/ddd-persistence-model-and-domain-model
도메인 계층 > 엔티티 > 지속성 무시
Are the benefits of pure DDD worth
the cost in
the speed of development?
도메인 계층 > 엔티티 > 지속성 무시
나도 잘 모르는데...
급진적 패러다임 변경을
팀에 설득할 자신이 없음
도메인 계층 > 엔티티 > 지속성 무시
도메인 계층 > 엔티티 > 지속성 무시
그 대신
도메인 계층 > 엔티티 > 지속성 무시
그 대신
할 수 있는 만큼은
POPO 처럼 쓸 수 있게 하자!
도메인 계층 > 엔티티 > 지속성 무시
엔티티에서 session 객체
사용 금지
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class Ad(Base):
def impression_count(self):
count = session.query(
func.sum(AdImpressionCount.value),
).filter(
AdImpressionCount.ad_id == self.id,
).scalar()
return count or 0
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class Ad(Base):
def impression_count(self):
count = session.query(
func.sum(AdImpressionCount.value),
).filter(
AdImpressionCount.ad_id == self.id,
).scalar()
return count or 0
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class TestAd(Base):
def test_impression_count(self):
# insert AdImpressionCount
ad = Ad()
count = ad.impression_count()
assert count == expected_count
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class TestAd(Base):
def test_impression_count(self):
# insert AdImpressionCount
ad = Ad()
count = ad.impression_count()
assert count == expected_count
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class Ad(Base):
impressions = relationship(AdImpressionCount)
def impression_count(self):
value = 0
for impression in self.impressions:
value += impression.value
return value
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class Ad(Base):
impressions = relationship(AdImpressionCount)
def impression_count(self):
value = 0
for impression in self.impressions:
value += impression.value
return value
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class Ad(Base):
impressions = relationship(AdImpressionCount)
def impression_count(self):
value = 0
for impression in self.impressions:
value += impression.value
return value
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class TestAd(Base):
def test_impression_count(self):
impressions = []
for _ in range(10):
impressions.append(AdImpressionCount())
ad = Ad(impressions=impressions)
count = ad.impression_count()
assert count == expected_count
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
class TestAd(Base):
def test_impression_count(self):
impressions = []
for _ in range(10):
impressions.append(AdImpressionCount())
ad = Ad(impressions=impressions)
count = ad.impression_count()
assert count == expected_count
도메인 계층 > 엔티티 > 지속성 무시 > session 금지
응용 계층
도메인 주도 설계: 에릭 에반스 (위키북스)
응용 계층
데이터 스토어에서 엔티티 조회
응용 계층
데이터 스토어에서 엔티티 조회
조회한 엔티티 사용 (요청 실행)
응용 계층
데이터 스토어에서 엔티티 조회
조회한 엔티티 사용 (요청 실행)
트랜잭션
응용 계층
광고는 기간을 변경할 수 있다
응용 계층
응용 계층 > 엔티티 조회
class AdService(object):
def change_period(self):
pass
class AdService(object):
def change_period(self, ad_id):
ad = session.query(Ad).get(ad_id)
응용 계층 > 엔티티 조회
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.start = start
ad.end = end
응용 계층 > 엔티티 조회
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.start = start
ad.end = end
X
응용 계층 > 엔티티 조회
응용 계층 > 엔티티 사용
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.start = start
ad.end = end
응용 계층 > 엔티티 사용
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.set_start(start)
ad.set_end(end)
class Ad(Base):
def set_start(self, start):
self.start = start
def set_end(self, end):
self.end = end
응용 계층 > 엔티티 사용
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.set_start(start)
ad.set_end(end)
class Ad(Base):
def set_start(self, start):
self.start = start
def set_end(self, end):
self.end = end
X
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
ad.change_period(start, end)


class Ad(Base):
def change_period(self, start, end):
self.start = start
self.end = end
응용 계층 > 엔티티 사용
응용 계층 > 트랜잭션
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
with session.begin():
ad.change_period(start, end)




class Ad(Base):
def change_period(self, start, end):
self.start = start
self.end = end
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
with session.begin():
ad.change_period(start, end)




class Ad(Base):
def change_period(self, start, end):
self.start = start
self.end = end
응용 계층 > 트랜잭션
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
with session.begin():
ad.change_period(start, end)




class Ad(Base):
def change_period(self, start, end):
self.start = start
self.end = end
응용 계층 > 트랜잭션
class AdService(object):
def change_period(self, ad_id, start, end):
ad = session.query(Ad).get(ad_id)
with session.begin():
ad.change_period(start, end)




class Ad(Base):
def change_period(self, start, end):
self.start = start
self.end = end
응용 계층 > 트랜잭션
잠깐!
응용 계층 > DIP
응용 계층도 도메인 계층처럼
지속성 정보를
포함해서는 안됩니다!
응용 계층 > DIP
DIP
(Dependency Inversion Principle)
응용 계층 > DIP
https://en.wikipedia.org/wiki/Dependency_inversion_principle
Flask / Django
안 할 겁니다
Flask 앱 만들기 (X)
만든 앱을 Flask로
제공하기 (O)
Flask는 거들 뿐
Flask는 거들 뿐
current_app
g
request
session
마무리
갈길이 (아주) 멀어요
아직 *유사* DDD이긴 하지만
복잡한 앱을 만들 때
좋은 지침이 되고 있습니다
의도가 명확해지는 느낌
문제 해결에 집중하고 있는 느낌
테스트도 잘 짜짐
테스트 속도도 빨라짐
여러분도 필요한 곳에
적절하게 활용하실 수 있기를
바랍니다
끝

지금 당장 (유사) DDD 시작하기