\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 00. Kick-Off\n",
"\n",
"## 얼음 깨기\n",
"\n",
"**[CyberMES 2027]**\n",
"\n",
"- 2027년의 MES 아키텍처는 어떻게 변할까요?\n",
"- 사이버 펑크의 시대가 과연 올까요?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 내 손안의 마이크로서비스"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 스터디 소개\n",
"\n",
"**[내용]**cd\n",
"\n",
"- 스터디 모토\n",
"- 스터디 미션\n",
"- 스터디 규칙\n",
"- 멤버 소개"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 스터디 모토\n",
"\n",
"- 무엇을 얻을것인지 생각\n",
"- 자랑하고 인정받고 칭찬받는 놀이터\n",
" - ~라는 최신 정보가 있어요 - 엄지척"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 스터디 미션\n",
"\n",
"- 메인 미션: 책 완독, 관심 내용 발표 1회이상\n",
"- 그 이외에는 자유\n",
"- 다양한 도전과제와 즐길거리 제공\n",
" - 서브 퀘스트: MSA 토이 프로젝트 만들어서 호스팅하기\n",
" - 사이드 퀘: AWS/Azure에서 MSA 서비스 호스팅, 다른 모던 언어 배워보기, DevOps 배우기, AWS 자격증, 라즈베리파이로 개인 클러스터 만들기, 모바일 앱 만들어 플레이스토에 배포하기 등\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 겁먹지 마세요\n",
"\n",
"- 책 내용이 어렵지만 다 이해할 필요 없어요.\n",
" - 책을 \"완독\" 하는게 목적이지 \"완성\"하는게 목적이 아닙니다.\n",
"- 메인 퀘스트가 어려우면 난이도를 낮추세요.\n",
" - 챕터에서 알고싶은 범위를 한정하거나, 이해할 챕터 분량을 한정하기\n",
"- 메인 퀘스트가 어려우면 서브/사이드 퀘스트에서 재미를 찾으세요!\n",
"- 이해 안가는 내용은 슬랙 / 컨플루언스에 언제든 물어보세요."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 추천 미션 예:\n",
"\n",
"다음중 하나를 해보고 스샷을 공유해주세요, 추천 수에 따라서 스벅 쿠폰을 보내드립니다(?)\n",
"\n",
"- 마음에 드는 언어 하나 골라서 도메인 모델링 예제 구현 해보기\n",
"- 파이썬 도커 개발 환경에 JupyterLab 셋팅해보기\n",
"- 클라우드 플랫폼 (AWS, Azure, 또는 GCP) 가입해서 가상머신에 앱 하나 띄워보기\n",
"- 책에서 감명깊게 본 부분 캡쳐해서 감상평 올리기~"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 교재 선정 이유\n",
"\n",
"https://github.com/cosmicpython/book\n",
"\n",
"- 거의 유일 무이한 파이썬 기반 아키텍처 북\n",
"- 실천적인 접근 방법: 이론만큼 구현에도 중점\n",
"- 최신 교재 / 최신 파이썬 기술 포함\n",
"- MSA를 맹신하지 않음.\n",
"- 엄청난 아마존 평점"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 스터디 규칙\n",
"\n",
"- 공휴일 / 연휴 제외 매주 모이는 것을 원칙으로 합니다.\n",
"- 매주 내드리는 메인 미션은 반드시 완수하셔야 합니다.\n",
"- 메인 미션은. 정해진 분량의 교재 범위를 읽어오시고\n",
"- 지난번 모임에서 읽은 챕터의 테스트 코드를 완성시켜오시는 것입니다.\n",
"- 스터디를 마치기 전까지 최소 1번 이상 발표를 하셔야 합니다"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 토론\n",
"\n",
"**[내용]**\n",
"\n",
"- 왜 마이크로서비스가 대세일까요?\n",
"- 왜 파이썬으로 하나요?\n",
"- 스터디 운영방법?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 왜 마이크로서비스가 대세일까요?\n",
"\n",
"- 마이크로서비스가 소프트웨어 복잡성을 해결하는 현재까지 가장 진보된 방법이기 때문에\n",
"- 거대한 모놀리식 서비스를 세부레벨로 찢어서 부서간의 사일로 해결\n",
"- 그럼에도 개별 서비스들의 영향의 컨테이너 레벨로 안전하게 격리하고 서비스 메시를 통해 스케일 아웃 가능\n",
"- SOA처럼 헤비한 프레임워크를필요로 하지 않아 간단하게 시작해볼 수 있음\n",
"- 마이크로서비스 운영 기술을 통해 엔터프라이즈 레벨로 확장이 용이"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 왜 파이썬으로 하나요?\n",
"\n",
"- 가장 인기있는 언어\n",
"- 가장 빨리 발전하고 있는 언어 중 하나\n",
"- 오너십과 파이썬 커뮤니티니의 건전성\n",
" - 리눅스와 비슷? PEP 읽어보면 귀도 반 로섬이 거의 항상 참여"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 효과적인 스터디 운영방법은?\n",
"\n",
"- 무엇을 공부하게 되나요?\n",
"- 너무 어렵지는 않을까요?\n",
"- 재미있게 열심히 할 수 있을까요?\n",
"- 회비 기반으로 동기부여하기\n",
" - 총무가 필요합니다. (매 주 5000원씩 회비를 걷습니다)\n",
" - 엄지척을 많이 받은 발표자에게 상품 쿠폰을 제공합니다."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 책 둘러보기\n",
"\n",
"- https://github.com/cosmicpython/book\n",
"\n",
"> Whenever we introduce an architectural pattern in this book, we’ll always ask,\n",
">\n",
"> “What do we get for this? And what does it cost us?”\n",
"\n",
"### Introduction\n",
"\n",
"- Why Do Our Designs Go Wrong?\n",
"- Encapsulation and Abstractions\n",
"- Layering\n",
"- The Dependency Inversion Principle\n",
"- A Place for All Our Business Logic: The Domain Model\n",
"\n",
"### I. Building an Architecture to Support Domain Modeling\n",
"\n",
"#### 1. Domain Modeling\n",
"\n",
"- What Is a Domain Model?\n",
"- Exploring the Domain Language\n",
"- Unit Testing Domain Models\n",
"- Not Everything Has to Be an Object: A Domain Service Function\n",
"\n",
"#### 2. Repository Pattern\n",
"\n",
"- Persisting Our Domain Model\n",
"- Some Pseudocode: What Are We Going to Need?\n",
"- Applying the DIP to Data Access\n",
"- Reminder: Our Model\n",
"- Introducing the Repository Pattern\n",
"- Building a Fake Repository for Tests Is Now Trivial!\n",
"- What Is a Port and What Is an Adapter, in Python?\n",
"\n",
"#### 3. A Brief Interlude: On Coupling and Abstractions\n",
"\n",
"- Abstracting State Aids Testability\n",
"- Choosing the Right Abstraction(s)\n",
"- Implementing Our Chosen Abstractions\n",
"\n",
"#### 4. Our First Use Case: Flask API and Service Layer\n",
"\n",
"- Connecting Our Application to the Real World\n",
"- A First End-to-End Test\n",
"- The Straightforward Implementation\n",
"- Error Conditions That Require Database Checks\n",
"- Introducing a Service Layer, and Using FakeRepository to Unit Test It\n",
"- Why Is Everything Called a Service?\n",
"- Putting Things in Folders to See Where It All Belongs\n",
"\n",
"#### 5. TDD in High Gear and Low Gear\n",
"\n",
"- How Is Our Test Pyramid Looking?\n",
"- Should Domain Layer Tests Move to the Service Layer?\n",
"- On Deciding What Kind of Tests to Write\n",
"- High and Low Gear\n",
"- Fully Decoupling the Service-Layer Tests from the Domain\n",
"- Carrying the Improvement Through to the E2E Tests\n",
"\n",
"#### 6. Unit of Work Pattern\n",
"\n",
"- The Unit of Work Collaborates with the Repository\n",
"- Test-Driving a UoW with Integration Tests\n",
"- Unit of Work and Its Context Manager\n",
"- Using the UoW in the Service Layer\n",
"- Explicit Tests for Commit/Rollback Behavior\n",
"- Explicit Versus Implicit Commits\n",
"- Examples: Using UoW to Group Multiple Operations into an Atomic Unit\n",
"- Tidying Up the Integration Tests\n",
"\n",
"#### 7. Aggregates and Consistency Boundaries\n",
"\n",
"- Why Not Just Run Everything in a Spreadsheet?\n",
"- Invariants, Constraints, and Consistency\n",
"- What Is an Aggregate?\n",
"- Choosing an Aggregate\n",
"- One Aggregate = One Repository\n",
"- What About Performance?\n",
"- Optimistic Concurrency with Version Numbers\n",
"- Testing for Our Data Integrity Rules\n",
"\n",
"### II. Event-Driven Architecture\n",
"\n",
"#### 8. Events and the Message Bus\n",
"\n",
"- Avoiding Making a Mess\n",
"- Single Responsibility Principle\n",
"- All Aboard the Message Bus!\n",
"- Option 1: The Service Layer Takes Events from the Model and Puts Them on the Message Bus\n",
"- Option 2: The Service Layer Raises Its Own Events\n",
"- Option 3: The UoW Publishes Events to the Message Bus\n",
"\n",
"#### 9. Going to Town on the Message Bus\n",
"\n",
"- A New Requirement Leads Us to a New Architecture\n",
"- Refactoring Service Functions to Message Handlers\n",
"- Implementing Our New Requirement\n",
"- Test-Driving a New Handler\n",
"- Optionally: Unit Testing Event Handlers in Isolation with a Fake Message Bus\n",
"\n",
"#### 10. Commands and Command Handler\n",
"\n",
"- Commands and Events\n",
"- Differences in Exception Handling\n",
"- Discussion: Events, Commands, and Error Handling\n",
"- Recovering from Errors Synchronously\n",
"\n",
"#### 11. Event-Driven Architecture: Using Events to Integrate Microservices\n",
"\n",
"- Distributed Ball of Mud, and Thinking in Nouns\n",
"- Error Handling in Distributed Systems\n",
"- The Alternative: Temporal Decoupling Using Asynchronous Messaging\n",
"- Using a Redis Pub/Sub Channel for Integration\n",
"- Test-Driving It All Using an End-to-End Test\n",
"- Internal Versus External Events\n",
"\n",
"#### 12. Command-Query Responsibility Segregation (CQRS)\n",
"\n",
"- Domain Models Are for Writing\n",
"- Most Users Aren’t Going to Buy Your Furniture\n",
"- Post/Redirect/Get and CQS\n",
"- Hold On to Your Lunch, Folks\n",
"- Testing CQRS Views\n",
"- “Obvious” Alternative 1: Using the Existing Repository\n",
"- Your Domain Model Is Not Optimized for Read Operations\n",
"- “Obvious” Alternative 2: Using the ORM\n",
"- SELECT N+1 and Other Performance Considerations\n",
"- Time to Completely Jump the Shark\n",
"- Changing Our Read Model Implementation Is Easy\n",
"\n",
"#### 13. Dependency Injection (and Bootstrapping)\n",
"\n",
"- Implicit Versus Explicit Dependencies\n",
"- Aren’t Explicit Dependencies Totally Weird and Java-y?\n",
"- Preparing Handlers: Manual DI with Closures and Partials\n",
"- An Alternative Using Classes\n",
"- A Bootstrap Script\n",
"- Message Bus Is Given Handlers at Runtime\n",
"- Using Bootstrap in Our Entrypoints\n",
"- Initializing DI in Our Tests\n",
"- Building an Adapter “Properly”: A Worked Example\n",
"\n",
"#### Epilogue\n",
"\n",
"- What Now?\n",
"- How Do I Get There from Here?\n",
"- Separating Entangled Responsibilities\n",
"- Identifying Aggregates and Bounded Contexts\n",
"- An Event-Driven Approach to Go to Microservices via Strangler Pattern\n",
"- Convincing Your Stakeholders to Try Something New\n",
"- Questions Our Tech Reviewers Asked That We Couldn’t Work into Prose\n",
"- Footguns\n",
"- More Required Reading\n",
"\n",
"#### Appendicies\n",
"\n",
"- A. Summary Diagram and Table\n",
"- B. A Template Project Structure\n",
"- C. Swapping Out the Infrastructure: Do Everything with CSVs\n",
"- D. Repository and Unit of Work Patterns with Django\n",
"- E. Validation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 참고: 프로그래밍 언어 랭킹\n",
"\n",
"#### PYPL 2020\n",
"\n",
"- https://pypl.github.io/PYPL.html\n",
"\n",
"1. **Python** (30.34% / **+1.2**%) -- *더 할말이?*\n",
"2. Java (17.23% / -1.7%)\n",
"3. JavaScript (8.65% / +0.6%) -- *[**TypeScript**]: 왕위를 계승중입니다...*\n",
"4. **C#** (6.44% / -0.8%) -- *.NET Framwork의 최강자*\n",
"5. C/C++ / 6.11% / +0.1%)\n",
"6. PHP (5.88% / -0.3%)\n",
"7. R (3.84% / +0.1%) -- *한때 유일하게 쓸만한 통계/데이터 과학 언어, [Jupyter의 3대장](https://blog.jupyter.org/i-python-you-r-we-julia-baf064ca1fb6?gi=186c617bcf06)*\n",
"8. Objective-C (3.75% / +1.2%)\n",
"9. Swift (2.17% / -0.3%)\n",
"10. Matlab (1.77% / -0.0%)\n",
"11. **TypeScript** (1.62% / -0.2%)\n",
"12. **Go** (1.52% / +0.3%) -- *마이크로서비스 트렌드의 대세*\n",
"13. **Kotlin** (1.44% / -0.2%) -- *JVM 기반 언어중 최고 대세*\n",
"14. Ruby (1.28% / -0.1%)\n",
"15. **Rust** (1.12% / +0.5%) -- *마이크로서비스 트렌드의 또다른 대세언어, 함수형 언어의 적자*\n",
"16. VBA (1.05% / -0.3%)\n",
"17. Scala (0.97% / -0.1%) -- *Kotlin에게 인기 빼앗기는 중...*\n",
"18. Visual Basic (0.67% / -0.3%)\n",
"19. Ada (0.61% / +0.3%)\n",
"20. Dart (0.58% / +0.2%) -- *Flutter 프레임웍으로 인기 급상승*\n",
"21. Lua (0.55% / +0.2%)\n",
"22. Perl (0.46% / -0.1%)\n",
"23. **Groovy** (0.43% / -0.0%) -- *최고 Cool한 JVM 언어였지만.. Kotlin/Scala에 인기 다뺏김 Gradle로 연명중*\n",
"24. Abap (0.41% / -0.1%)\n",
"25. **Julia** (0.33% / +0.1%) -- *Data Scientist 들에게 사랑받는 언어, [Jupyter의 3대장](https://blog.jupyter.org/i-python-you-r-we-julia-baf064ca1fb6?gi=186c617bcf06)*\n",
"26. **Haskell** (0.27% / -0.0%)\n",
"27. Cobol (0.26% / -0.1%)\n",
"28. Delphi (0.26% / +0.0%)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3분 DDD"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 도메인 모델링이란\n",
"\n",
"#### Domain modeling\n",
"\n",
"- The part of your code that is closest to the business.\n",
"- The most likely to change, and the place where you deliver the most value to the business.\n",
"- Need to make it easy to understand and modify.\n",
"\n",
"#### Distinguish entities from value objects\n",
"\n",
"**Value Objects**\n",
"- Defined by its attributes. \n",
"- Usually best implemented as an immutable type. \n",
" - If you change an attribute on a Value Object, it represents a different object. \n",
" \n",
"**Entities**\n",
"- Even though an entity has attributes that may vary over time, it will still be the same entity. \n",
"- Important to define what does uniquely identify an entity \n",
" - usually some sort of name or reference field).\n",
"\n",
"#### Not everything has to be an object\n",
"\n",
"- The \"verbs\" in your code be functions. \n",
" - e.g) `FooManager, BarBuilder, BazFactory`\n",
" - vs.\n",
" - `manage_foo(), build_bar(), get_baz()`\n",
"\n",
"#### This is the time to apply your best OO design principles\n",
"\n",
"- Revisit the SOLID principles\n",
"- All the other good heuristics like \"has a versus is-a,\" \"prefer composition over inheritance,\" and so on.\n",
"\n",
"#### You’ll also want to think about consistency boundaries and aggregates\n",
"But that’s a topic for [chapter_07_aggregate].\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Value Object in Python"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from dataclasses import dataclass\n",
"\n",
"@dataclass(frozen=True)\n",
"class User:\n",
" name: str\n",
" age: int\n",
" \n",
"user1 = User('John', 16)\n",
"user2 = User(age=16, name='John')\n",
"\n",
"user1 == user2 # True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 참고: 모던 프로그래밍 언어에서 Value Object 구현 방법"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### C# 9 (.NET 5.0)\n",
"\n",
"```c#\n",
"public record User(String Name, int Age);\n",
"\n",
"var user1 = new User(\"John\", 16);\n",
"var user2 = new User(\"John\", 16);\n",
"user1 == user2\n",
"\n",
"public record User2 {\n",
" public String Name { get; init; }\n",
" public int Age { get; init; }\n",
"};\n",
"\n",
"var user1 = new User2 { Name=\"Love\", Age=17 };\n",
"var user2 = new User2 { Age=17, Name=\"Love\" };\n",
"user1 == user2\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### TypeScript\n",
"\n",
"```typescript\n",
"// `ValueObject` 구현 방법은 별도 노트북을 참조하세요.\n",
"interface UserProps {\n",
" name: String\n",
" age: number\n",
"}\n",
"\n",
"class User extends ValueObject {\n",
" constructor(props) { super(props) }\n",
"}\n",
"\n",
"const user1 = new User({name: 'John', age: 16})\n",
"const user2 = new User({age: 16, name: 'John'})\n",
"\n",
"user1 === user2 // false\n",
"user1.equals(user2) // true\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Go\n",
"```go\n",
"type User struct { \n",
" name string\n",
" age int\n",
"}\n",
"\n",
"user1 := User { \"John\", 16 }\n",
"user2 := User { \"John\", 16 }\n",
"\n",
"user1 == user2 // true\n",
"\n",
"// 당황: Go는 Immuable 속성을 지원하지 않아서 동적으로 속성을 변경 가능.\n",
"\n",
"user1.name = \"Test\"\n",
"user1 == user2 // false\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Kotlin\n",
"\n",
"```kotlin\n",
"data class User(val name: String, val age: Int)\n",
"\n",
"val user1 = User(\"John\", 10)\n",
"val user2 = User(\"John\", 10)\n",
"\n",
"user1 == user2 // true\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Rust\n",
"\n",
"```rust\n",
"##[derive(PartialEq, Eq)]\n",
"struct User { name: String, age: u16 }\n",
"\n",
"let user1 = User { name: String::from(\"John\"), age: 16 };\n",
"let user2 = User { name: String::from(\"John\"), age: 16 };\n",
"\n",
"user1 == user2 // true\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Groovy\n",
"\n",
"```groovy\n",
"import groovy.transform.Immutable\n",
"\n",
"@Immutable class User {\n",
" String name\n",
" int age\n",
"}\n",
"\n",
"final user1 = new User('John', 16)\n",
"final user2 = new User([age: 16, name: 'John'])\n",
"\n",
"user1 == user2 // true\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Julia\n",
"\n",
"```julia\n",
"import Base.@kwdef\n",
"\n",
"@kwdef struct User\n",
" name::String\n",
" age::UInt8\n",
"end\n",
"\n",
"user1 = User(\"John\", 16)\n",
"user2 = User(age=16, name=\"John\")\n",
"\n",
"user1 == user2 # true\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### Haskell\n",
"\n",
"```haskell\n",
"data User = User \n",
" { name :: String, \n",
" age :: Int \n",
" } deriving (Eq, Show)\n",
" \n",
"user1 = User { name = \"John\", age = 16 }\n",
"user2 = User { age = 16, name = \"John\" }\n",
"user1 == user2 -- True\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 결론\n",
"\n",
"- 3개월 안에 책을 다 읽을 겁니다\n",
"- 3개월 동안 MSA 토이 프로젝트를 완성하고 발표를 하게 될 거에요.\n",
"- 스터디는 정말 재밌게 할 수 있을 겁니다!\n",
"- 특별한 일 없으면 매 주 오프라인/온라인 교대로 만날거에요.\n",
"- 다음주에 또 만나요 :D"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
},
"toc-autonumbering": false,
"toc-showcode": false,
"toc-showtags": false,
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"state": {},
"version_major": 2,
"version_minor": 0
}
}
},
"nbformat": 4,
"nbformat_minor": 4
}