Event Storming: วิธีออกแบบ Software Architecture Partitioning
ต้องอ่าน 2 บทความนี้ก่อน what-is-software-architecture Event-storming-กับคนแคระทั้งเจ็ด-ตอนที่-1
อย่างที่เคยเล่าไปก่อนหน้านี้ว่าเราไม่สามารถอธิบาย Software Architecture ได้จากมุมมองใดมุมมองหนึ่ง จากหนังสือ Fundamentals of Software Architecture ได้อธิบายไว้ 4 แกนคือ
- Architecture Characteristics
- Logical Components
- Architecture Style
- Architecture Decisions
หากเราต้องออกแบบระบบ จุดท้าทายแรกคือการมองหา Architecture Characteristics ตรงนี้จะมี 2 คำที่ต้องรู้จักคือ Implicit และ Explicit

Explicit Characteristics: ตามตำราเรียนสมัยมหาลัยก็คือ Functional Requirements (FRs) คือสิ่งที่ระบุว่า “ระบบต้องทำอะไรได้บ้าง” เพื่อตอบโจทย์ธุรกิจโดยตรง
ตัวอย่างเช่น ลูกค้าสมัครสมาชิกยิมได้, สแกนคิวอาร์โค้ดผ่านประตูเข้าสตูดิโอได้, หรือส่งข้อมูลสัญญาไปตัดเงินผ่านเกตเวย์ภายนอกได้ เมื่ออ่านเอกสาร เข้าใจ Requirements แล้ว ลำดับต่อไปคือแปลงเป็น Characteristics ให้ได้ จากตัวอย่างเมื่อสักครู่จะได้ประมาณนี้
- Extensibility & Evolvability: ระบบต้องออกแบบแยกส่วน เพื่อให้พร้อมต่อยอดบริการใหม่ๆ ในอนาคต (เช่น จองเทรนเนอร์, วางแผนอาหาร) ได้ง่าย โดยไม่ทำลายโครงสร้างเดิม
- Availability: ระบบสแกนคิวอาร์โค้ดเข้าสตูดิโอต้องพร้อมใช้งานสูง ห้ามล่มเด็ดขาด เพื่อไม่ให้กระทบต่อสิทธิ์และการเข้าใช้งานของลูกค้า
- Interoperability: ระบบต้องเชื่อมต่อและรับส่งข้อมูลสัญญาร่วมกับระบบจ่ายเงินภายนอก (External Payment Gateway) ได้อย่างแม่นยำและราบรื่น
Implicit Characteristics: Non-Functional Requirements (NFRs) คือสิ่งที่จำเป็นต้องมีเพื่อให้ “ระบบใช้งานได้จริง” Implicit มักเป็นสิ่งที่ธุรกิจไม่ได้ร้องขอหรือไม่มีระบุใน Requirements
ตัวอย่างเช่น เราต้องมี CI/CD แยก Environment เพื่อให้ง่ายต่อการพัฒนา หรือต้องมีระบบ Monitor อย่าง Grafana เพื่อให้มองเห็นสถานะการทำงานและแก้ไขได้ทันก่อนระบบมีปัญหา (Observability)

สมมุติว่าผมกำลังพัฒนาระบบ Gym Application หลังจากอ่าน Requirements document, ทำ User Story เพื่อทำความเข้าใจ Problem Domain จนได้ Top 3 Driving Characteristics ตามนี้
- Extensibility
- Availability
- Interoperability
และ Implicit Characteristics ตามนี้
- Maintainability
- Observability
ขั้นตอนต่อไปคือการหา Architecture Style โดย Architecture Style จะต้องเข้าใจอีก 4 เรื่องคือ
- Deployment Mode
- Partitioning
- Structure
- Communication
**Deployment Mode **หากมองผ่าน Deployment Mode โลกใบนี้มี Software Architecture Style อยู่แค่ 2 แบบ คือ Monolith และ Distributed โดยใช้ทฤษฎีตามนี้

Monolith: ตามทฤษฎีคือ เรา Deploy ระบบแบบ 1 Unit (อิหยังวะ) จริงๆ แล้วมันคือเรารัน 1 Process ID (PID) ตอน Deploy ในสมัยอ้าย ให้นึกถึงระบบที่พัฒนาด้วย PHP, C# หรือ Java ที่เป็น Server-Side Rendering (SSR) มัดรวมทุกอย่างเป็นทั้ง Presentation, Business และ Persistence เป็นก้อนเดียวแล้วนำไป Deploy นี่คือที่มาของคำว่า 1 Unit
Distributed: ตามทฤษฎีคือ เรา Deploy ระบบแบบ Multiple Unit คือการแยกระบบออกเป็นเรื่องย่อยๆ ตาม Business หรือ Technical แล้วแต่ Principle ที่เราเลือกใช้ นั่นหมายความว่าเราจะมี Process ID (PID) ตามเรื่องๆ สุดเบอร์ของโลก Distributed คือ Architecture ที่เราคุ้นเคยกัน คือ Microservices นี่คือเหตุผลว่าทำไมมันถึงยาก เพราะเริ่มมาคนส่วนมากมักไปขวาสุดเลย (ขอแปะถึงแค่ตรงนี้ จะค่อยๆ เล่าให้ฟัง)
หลักการในการเลือก Architecture Style คือเราต้องเลือกจาก Architecture Characteristics โดย Mark Richards มีเอกสารอ้างอิงตามด้านล่าง ดูที่ด้านซ้ายจะมี Characteristics ต่างๆ ให้เราวิเคราะห์จาก Top 3 Driving Characteristics และ Implicit Characteristics ก่อนหน้านี้

**Architecture partitioning **เมื่อเราเลือก Software Architecture Style ได้แล้ว ไม่ว่าจะเป็น Monolith หรือ Distributed สิ่งที่ต้องทำต่อไปคือการหา Partitioning จุดประสงค์หลักเพื่อจับแยกระบบขนาดใหญ่ที่ซับซ้อนออกเป็นส่วนย่อยๆ และเริ่ม Problem Solving เพื่อหาว่าจุดที่เราต้อง Focus โดยหากสังเกตเอกสาร Architecture Style จะมีแกนของ Partitioning ที่ระบุว่า Technical หรือ Domain นี่คืออีก 2 ส่วนที่ต้องเข้าใจ
Technical Partitioning: คือการแบบกลุ่มคอมโพเนนต์ของระบบบตาม Technical capabilities
ตัวอย่างที่นิยมคือ N-Tier Architecture (หรือ Layered Architecture) เช่น แบ่งออกเป็นชั้น Presentation, Business rules, Services และ Persistence
- ข้อดี: แยกความกังวลทางเทคนิคชัดเจน โครงสร้างโค้ดเป็นระเบียบ และสอดคล้องกับการจัดทีมตามความเชี่ยวชาญ (เช่น Frontend, Backend, DBA)
- ข้อเสีย: Domain Smearing เพราะฟีเจอร์ธุรกิจหนึ่งเรื่อง (เช่น ระบบสมัครสมาชิกยิม หรือระบบสแกนคิวอาร์โค้ดเข้าสตูดิโอ) ต้องเขียนโค้ดกระจายทะลุทุกเลเยอร์ เวลาแก้ไขจึงต้องตามไปแก้ในหลายๆ ชั้น
Domain Partitioning: คือการจับกลุ่มคอมโพเนนต์ของระบบตามโดเมนหรือ Business Workflow เป็นหลัก ตัวอย่าง Principles ที่นิยมคือแนวคิด DDD (Domain-Driven Design) โดยแบ่งระบบออกเป็น Subdomain และ Bounded Contexts เช่น ในระบบยิมจะแยกส่วนเป็น ระบบจัดการสมาชิก (Membership), ระบบจองคลาสและเทรนเนอร์ (Booking) และระบบประเมินสุขภาพ (Fitness Assessment)
- ข้อดี: โครงสร้างระบบสะท้อนภาพธุรกิจชัดเจน และเอื้อต่อการสร้างทีมแบบ Cross-functional ที่พัฒนาฟีเจอร์ยิมนั้นๆ ได้จบในทีมเดียวตั้งแต่ต้นจน Production Release
- ข้อเสีย: การแยกขาดจากกัน อาจทำให้ต้องยอมเขียนโค้ด Duplication ในหลายๆ โดเมน เพื่อแลกมาด้วยความเป็นอิสระของแต่ละโดเมน ศัพท์เท่ๆ แถวนี้เราเรียกว่า Independent Deployment
ในที่สุดก็มาถึงเนื้อหาของบทความนี้ ตรง Architecture partitioning นี่แหละคือจุดที่ Event Storming เข้ามาช่วยได้ ไม่ว่าเราจะใช้ Architecture Style แบบ Technical หรือ Domain
มาดูตัวอย่าง Gym Case Study ถึงตรงนี้เราสามารถใช้ Event Storming แค่ Big Picture Level ก็สามารถแบ่ง Partitioning ได้แล้ว มาดู ความต้องการเบื้องต้นของระบบคือ การสร้างและโปรโมตข้อเสนอ (Offers), การทำสัญญา (Contracting), การออกบัตรผ่านเข้ายิม (Pass Registration)
หากแบ่งตาม Technical Partitioning และ Domain Partitioning จะได้ตามด้านล่าง
**Technical Partitioning **หากเราเลือกท่านี้ โค้ดคอมโพเนนต์ของทั้ง 3 ฟีเจอร์ยิมจะถูกหั่นและแยกกลุ่มกระจายกันอยู่ตามหน้าที่ทางเทคนิค ทำให้เกิดปัญหา Domain Smearing ที่เวลามีการเปลี่ยนแปลงฟีเจอร์ธุรกิจหนึ่งเรื่อง (เช่น ปรับปรุงระบบสัญญายิม) จะต้องตามไปแก้ไขโค้ดที่กระจายอยู่ข้ามเลเยอร์ทั้งหมด
- Presentation Layer: offering.controller, contracting.controller, pass-registration.controller
- Business Rules / Service Layer: offering.service, contracting.service, pass-registration.service
- Persistence Layer: offering.repo, contracting.repo, pass-registration.repo

Domain Partitioning หากเราเลือกท่านี้ผลลัพธ์จาก Event Storming จะมาจัดกลุ่มคอมโพเนนต์ตามโดเมนธุรกิจโดยตรง คอมโพเนนต์ทางเทคนิคทั้งหมด (ไม่ว่าจะเป็น Controller, Service หรือ Repo) ของฟีเจอร์นั้นๆ จะถูกรวบรวมรวดเดียวจบอยู่ภายใต้กล่องโดเมนของตัวเอง
- Offering Context: รวบรวมทุกส่วนที่เกี่ยวกับข้อเสนอของยิมไว้ด้วยกัน
- Contracting Context: รวบรวมทุกส่วนที่เกี่ยวกับขั้นตอนการทำสัญญาสมาชิกไว้ด้วยกัน
- Pass Registration Context: รวบรวมทุกส่วนที่เกี่ยวกับการออกบัตรและสิทธิ์การเข้ายิมไว้ด้วยกัน
Conclusion
จากที่เราไล่เรียงกันมาในข้างต้น ตอนนี้เราได้เคลียร์ภาพของ Deployment Mode และ Partitioning กันไปเป็นที่เรียบร้อยแล้ว เพื่อไม่ให้เนื้อหาในตอนนี้ยาวเหยียดจนแน่นเป็นหน้ากระดาษเกินไป ในส่วนของอีก 2 มิติที่เหลืออย่าง Structure และ Communication ไว้มาลุยกันต่อในบทความถัดไป ถ้าจะอธิบายคร่าวๆ มันคือส่วนที่ Developer จะเริ่มคุ้นเคยกัน
- Structure จะเริ่มไปพูดถึง MVC, Clean Architecture, Port and Adapter Architecture และเราใช้ Principles อะไรในการวางโครงสร้างโค้ด
- Communication ระหว่าง Multiple Unit เราจะคุยกันอย่างไร ใช้ TCP, Event หรือบลาๆ ถ้าในโลก DDD ก็คือส่วนของ Context Map
เจอกันในบทความตอนต่อไป