Contenu connexe Similaire à 設計/コンポーネント設計(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第22回】 (20) Plus de Tomoharu ASAMI (8) 設計/コンポーネント設計(3) 【クラウドアプリケーションのためのオブジェクト指向分析設計講座 第22回】2. SimpleModeling2021
• オブジェクト指向分析設計での共通範囲
• UML/UP
• 本講座で使⽤するUMLプロファイル
• プロファイル:SimpleModeling2021 (SM2021)
• オブジェクト指向分析設計の基本からの拡張部を明確化
• アジャイル開発
• Communication
• Embrace Change
• Travel Light
• Scaling
• Component-Based Development
• クラウド・アプリケーション
• モデル駆動開発
SM2021
Travel Light
Embrace Change
Cloud
Model-Driven
Scaling
CBD
3. 第1部 基本編の構成
• 概論 [第1回]
• 開発プロセス [第2回]
• 基本モデル [第3回]
• 静的モデル(1) [第4回]
• 静的モデル(2) [第5回]
• 動的モデル [第6回]
• 協調モデル [第7回]
• 関数モデル [第8回]
• 物理モデル [第9回]
• 作業分野 [第10回]
• ビジネス・モデリング [第11回]
• 要求 [第12回]
• 要求/ユースケース [第13回]
• 要求/シナリオ [第14回]
• 分析 [第15回]
• 分析/コンポーネント分析 [第16回]
• 分析/イベント駆動 [第17回]
• 作業分野
• 設計 [第18回]
• 設計/アーキテクチャ設計 [第19回]
• 設計/コンポーネント設計(1) [第20回]
• 設計/コンポーネント設計(2) [第21回]
• 設計/コンポーネント設計(3) [第22回]
• 設計/ドメイン設計 [第23回]
• 設計/ UX/UI設計 [第24回]
• 実装 [第25回]
• テスト [第26回]
• アプリケーション・アーキテクチャ [第27回]
• ドメイン・モデル [第28回]
• アプリケーション・モデル [第29回]
• プレゼンテーション・モデル [第30回]
• ケーススタディ[第31回]
• 要求モデル [第32回]
• 分析モデル [第33回]
• 設計モデル [第34回]
• 実装 [第35回]
• テスト [第36回]
4. 本講座のアプローチ
• オブジェクト指向分析設計の基本を確認
• UML + UP(Unified Process)
• CBD (Component-Based Development)
• 最新技術でアップデート
• クラウド・コンピューティング
• イベント駆動、分散・並列
• ビッグデータ、AI、IoT
• コンテナ
• 関数型
• OFP(Object-Functional Programming), Reactive Streams
• ルール, AI
• DevOps
• アジャイル開発
• DX (Digital Transformation)
第25回 アプリケーション・アーキテクチャ
第2回 開発プロセス
第9回 物理モデル
第11回 ビジネス・モデリング
第2部 クラウド・アプリケーション編
第21回 設計/ドメイン設計
第20回 設計/コンポーネント設計
第2部 クラウド・アプリケーション編
5. 本講座の選択
• プログラミング⾔語
• Scala
• Javaエコシステム
• Pythonを併⽤
• コンテナ
• Docker/Kubernetes
• CI/CD
• Daggar
• ミドルウェア
• AWS
• クラウド・プラットフォーム
• AWS
• アプリケーション・フレームワーク
• 本講座が定義する仮想的なアプリケーション・フレームワーク
• コンポーネント・フレームワーク
• クラウド・アプリケーション
• 関数型
6. 原理 (Principle)
• Agile Software Development [ASD]
• SRP (The Single Responsibility Principle)
• OCP (The Open-Close Principle)
• LSP (The Liskov Substitution Principle)
• …
• GRASP (General Responsibility Assignment Software Patterns or Principals)
• Low Coupling
• High Cohesion
• …
• Writing Effective Use Cases [WEUC]
• Scope
• …
7. パターン (Pattern)
• Design Patterns [DP]
• Observer, Strategy, …
• Domain Driven Design [DDD]
• Ubiquitous Language, Intention-
Revealing Interfaces, …
• Analysis Patterns [AP]
• Party, Quantity, …
• Pattern-Oriented Software
Architecture [POSA]
• Layers, Pipes and Filters, …
• Patterns of Enterprise
Application Architecture [PEAA]
• Unit of Work, Data Transfer Object,
…
• Enterprise Integration Patterns
[EIP]
• Message Bus, Aggregator, …
• Patterns for Effective Use
Cases [PEUC]
• CompleteSingleGoal,
VerbPhraseName, …
• AntiPatterns [AnP]
• Stovepipe System, Analysis
Paralysis, …
20. package reservation
import scala.util.Try
import componentframework.ExecutionContext
trait IReservationService {
def reserve(cmd: ReserveCommand)
(using ctx: ExecutionContext): Try[ReserveResult]
def unreserve(cmd: UnreserveCommand)
(using ctx: ExecutionContext): Try[ReserveResult]
def getReservation(id: ReservationId)
(using ctx: ExecutionContext): Try[Option[Reservation]]
def queryReservation(q: ReservationQuery)
(using ctx: ExecutionContext): Try[Vector[Reservation]]
}
IReservationService.scala
21. package reservation
import scala.util.Try
import componentframework.ExecutionContext
trait IReservationManagement {
// ...
}
package reservation
trait IReservationMetrix {
def numberOfCacheSlots: Int
def numberOfCacheHit: Int
}
package reservation
import scala.util.Try
import componentframework.ExecutionContext
trait IReservationAdmin {
// ...
}
IReservationManagement.scala IReservationAdmin.scala
IReservationMetrix.scala Serviceability
Serviceability
22. package reservation
import java.time.LocalDateTime
import spire.math.Interval
case class ReservationQuery(
resourceId: Option[ResourceId],
userId: Option[UserId],
interval:
Option[Interval[LocalDateTime]]
) {
}
case class ReserveCommand(
resourceId: ResourceId,
userId: UserId,
interval: Interval[LocalDateTime]
) {
}
case class UnreserveCommand(
id: ReservationId
) {
}
case class ReserveResult(
) {
}
case class UnreserveResult(
) {
}
Command.scala
23. package reservation
case class ReservationId(id: String) extends AnyVal {
}
case class ResourceId(id: String) extends AnyVal {
}
case class UserId(id: String) extends AnyVal {
}
DataType.scala
25. package reservation
…
class ReservationService @Inject()(
val rule: ReservationService.ReservationRule,
val datastore: IDataStore
) extends IReservationService, IReservationManagement, IReservationAdmin, IReservationMetrix, EventListener {
// IReservationService
def reserve(cmd: ReserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def unreserve(cmd: UnreserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def getReservation(id: ReservationId)(using ctx: ExecutionContext): Try[Option[Reservation]] = ???
def queryReservation(q: ReservationQuery)(using ctx: ExecutionContext): Try[Vector[Reservation]] = ???
// IReservationManagement
// IReservationAdmin
// IReservationMetrix
def numberOfCacheSlots: Int = ???
def numberOfCacheHits: Int = ???
ReservationService.scala (1/2)
26. // EventListener
def receive(evt: Event): Unit = Option(evt) collect {
case m: ReservedEvent => reserved(m)
case m: UnreservedEvent => unreserved(m)
}
def reserved(evt: ReservedEvent): Unit = ???
def unreserved(evt: UnreservedEvent): Unit = ???
}
object ReservationService {
case class ReservationRule(
)
object ReservationRule {
def create(p: Config): ReservationRule = ???
}
}
ReservationService.scala (2/2)
29. trait IReservationService {
protected def invariants: Try[Unit]
def reserve(cmd: ReserveCommand)
(using ctx: ExecutionContext): Try[ReserveResult] =
for {
_ <- invariants
_ <- reserve_precondition(cmd)
r <- do_reserve(cmd)
_ <- reserve_postcondition(cmd, r)
_ <- invariants
} yield r
protected def reserve_precondition(cmd: ReserveCommand)
(using ctx: ExecutionContext): Try[Unit]
protected def reserve_postcondition(
cmd: ReserveCommand, result: ReserveResult
)(using ctx: ExecutionContext): Try[Unit]
protected def do_reserve(cmd: ReserveCommand)(using ctx: ExecutionContext):
Try[ReserveResult]
}
IReservationService.scala
DbCの実現例:インタフェース側に実装
第8回 関数モデル
30. case class ReservationId(id: String) {
require (ReservationId.validate(id), s"Bad id: $id")
}
object ReservationId {
def validate(id: String): Boolean = ???
def create(id: String): Try[ReservationId] =
if (validate(id))
Success(ReservationId(id))
else
Failure(new IllegalArgumentException(s"Bad id: $id"))
}
制約を⽤いてデータタイプのバリデーション
ReservationId.scala
31. case class ReservationQuery(
resourceId: Option[ResourceId],
userId: Option[UserId],
interval: Option[Interval[LocalDateTime]]
) {
require (ReservationQuery.validate(resourceId, userId, interval), s"Bad query: $this")
}
object ReservationQuery {
def validate(
resourceId: Option[ResourceId],
userId: Option[UserId],
interval: Option[Interval[LocalDateTime]]
): Boolean = ???
def create(
resourceId: Option[ResourceId],
userId: Option[UserId],
interval: Option[Interval[LocalDateTime]]
): Try[ReservationQuery] =
if (validate(resourceId, userId, interval))
Success(ReservationQuery(resourceId, userId, interval))
else
Failure(new IllegalArgumentException(s"Bad query: $resourceId, $userId, $interval"))
}
制約を⽤いてDTOのバリデーション
ReservationQuery.scala
38. // EventListener
def receive(evt: Event): Unit = Option(evt) collect {
case m: ReservedEvent => reserved(m)
case m: UnreservedEvent => unreserved(m)
}
def reserved(evt: ReservedEvent): Unit = ???
def unreserved(evt: UnreservedEvent): Unit = ???
}
object ReservationService {
case class ReservationRule(
)
object ReservationRule {
def create(p: Config): ReservationRule = ???
}
}
ReservationService.scala (2/2)
44. package reservation
…
class ReservationService @Inject()(
val rule: ReservationService.ReservationRule,
val datastore: IDataStore
) extends IReservationService, IReservationManagement, IReservationAdmin, IReservationMetrix,
EventListener {
// IReservationService
def reserve(cmd: ReserveCommand)
(using ctx: ExecutionContext): Try[ReserveResult] = ???
def unreserve(cmd: UnreserveCommand)
(using ctx: ExecutionContext): Try[ReserveResult] = ???
def getReservation(id: ReservationId)
(using ctx: ExecutionContext): Try[Option[Reservation]] = ???
def queryReservation(q: ReservationQuery)
(using ctx: ExecutionContext): Try[Vector[Reservation]] = ???
// IReservationManagement
// IReservationAdmin
// IReservationMetrix
def numberOfCacheSlots: Int = ???
def numberOfCacheHits: Int = ???
ReservationService.scala (1/2)
45. package reservation
import javax.inject.Singleton
import com.google.inject.AbstractModule
import com.typesafe.config.Config
import reservation.ReservationService.ReservationRule
import reservation.datastore.IDataStore
import reservation.datastore.DataStore
class ReservationServiceModule(config: Config) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[IDataStore]).to(classOf[DataStore]).in(classOf[Singleton])
bind(classOf[ReservationRule]).toInstance(ReservationRule.create(config))
}
}
ReservationServiceModule.scala
50. package reservation
…
class ReservationService @Inject()(
val rule: ReservationService.ReservationRule,
val datastore: IDataStore
) extends IReservationService, IReservationManagement, IReservationAdmin, IReservationMetrix, EventListener {
// IReservationService
def reserve(cmd: ReserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def unreserve(cmd: UnreserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def getReservation(id: ReservationId)(using ctx: ExecutionContext): Try[Option[Reservation]] = ???
def queryReservation(q: ReservationQuery)(using ctx: ExecutionContext): Try[Vector[Reservation]] = ???
// IReservationManagement
// IReservationAdmin
// IReservationMetrix
def numberOfCacheSlots: Int = ???
def numberOfCacheHists: Int = ???
51. package reservation
import javax.inject.Singleton
import com.google.inject.AbstractModule
import com.typesafe.config.Config
import reservation.ReservationService.ReservationRule
import reservation.datastore.IDataStore
import reservation.datastore.DataStore
class ReservationServiceModule(config: Config) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[IDataStore]).to(classOf[DataStore]).in(classOf[Singleton])
bind(classOf[ReservationRule]).toInstance(ReservationRule.create(config))
}
}
ReservationServiceModule.scala
53. 変化点 (variation point)
• 外部ファイルや環境変数による定義を変化点に設定する⽅式が
ポイント
• コンポーネント・フレームワークの機能を使⽤するのがよい
• 今回は変化点を集めたルールオブジェクトを作成し、DIで設定
する⽅式で実現
• 部品化、テスト容易性のためにはできるだけ疎結合な設定⽅式
が望ましい Testability
56. package reservation
…
class ReservationService @Inject()(
val rule: ReservationService.ReservationRule,
val datastore: IDataStore
) extends IReservationService, IReservationManagement, IReservationAdmin, IReservationMetrix, EventListener {
// IReservationService
def reserve(cmd: ReserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def unreserve(cmd: UnreserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def getReservation(id: ReservationId)(using ctx: ExecutionContext): Try[Option[Reservation]] = ???
def queryReservation(q: ReservationQuery)(using ctx: ExecutionContext): Try[Vector[Reservation]] = ???
// IReservationManagement
// IReservationAdmin
// IReservationMetrix
def numberOfCacheHit: Int = ???
def numberOfCacheMiss: Int = ???
def numberOfOperationCalls: Int = ???
57. package reservation
import javax.inject.Singleton
import com.google.inject.AbstractModule
import com.typesafe.config.Config
import reservation.ReservationService.ReservationRule
import reservation.datastore.IDataStore
import reservation.datastore.DataStore
class ReservationServiceModule(config: Config) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[IDataStore]).to(classOf[DataStore]).in(classOf[Singleton])
bind(classOf[ReservationRule]).toInstance(ReservationRule.create(config))
}
}
ReservationServiceModule.scala
62. package reservation
…
class ReservationService @Inject()(
val rule: ReservationService.ReservationRule,
val datastore: IDataStore
) extends IReservationService, IReservationManagement, IReservationAdmin, IReservationMetrix, EventListener {
// IReservationService
def reserve(cmd: ReserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def unreserve(cmd: UnreserveCommand)(using ctx: ExecutionContext): Try[ReserveResult] = ???
def getReservation(id: ReservationId)(using ctx: ExecutionContext): Try[Option[Reservation]] = ???
def queryReservation(q: ReservationQuery)(using ctx: ExecutionContext): Try[Vector[Reservation]] = ???
// IReservationManagement
// IReservationAdmin
// IReservationMetrix
def numberOfCacheSlots: Int = ???
def numberOfCacheHists: Int = ???
package reservation
trait IReservationMetrix {
def numberOfCacheSlots: Int
def numberOfCacheHit: Int
}
IReservationMetrix.scala
ReservationService.scala (1/2)
78. ドキュメント
• 仕様書
• UMLモデル+補⾜記事で仕様書を作成
• リファレンス・マニュアル
• プログラムから⾃動⽣成 (Scaladoc/Javadoc)
• ユーザー・マニュアル
• ユースケースのシナリオを実際のプログラムで実現できるための利⽤
者とシステムの対話をドキュメント化
• ドキュメントは散逸しやすいので、ソースコードと同じバー
ジョン管理の配下で管理するとよい
$ sbt doc
81. 参考⽂献
• The Unified Modeling Language Reference
Manual, 2nd (Rumbaugh他, 2004)
• The Unified Modeling Language User Guide,
2nd (Booch他, 2004)
• The Unified Software Development Process
(Jacobson他, 1999)
• The Object Constraint Language, 2nd (Warmer
他, 2003)
• UML 2 and the Unified Process: Practical
Object-Oriented Analysis and Design (Arlow
他, 2005)
• OMG Unified Modeling Language Version 2.5
(OMG, 2015)
• 上流⼯程UMLモデリング (浅海, 2008)