Scalaの型について(応用編その1)

タム

2024.01.26

20

こんにちは。タムです。

以前4回に渡ってScalaの型についてまとめてきました。

ここまででなんとなくわかった気にはなったのですが、実践に応用してみたい気がします。

というわけで、(前回の知識を使うかどうかわからないのですが)例としてPub/Subの実装をしてみようと思います。

なぜPub/Subかというと、DDDの構成要素の中でも特に自分がよく理解できてない気がするからです。

実装例ですが抽象的なデザインパターンの例だと現実味がなくて面白くないので、もう少しリアルなテーマで考えてみます。

いきなり完璧な実装はできないと思うので、今回もシリーズ物にして回を重ねるごとに改良していく形にしようと思います。

ミーティング予約サービス

初回実装

例として、ミーティング予約サービスを考えます。

このサービスは、ユーザからのミーティング予約リクエストを受けて、ミーティング情報を保存し、後から参照できるようにします。

また、ミーティングを予約したユーザ(主催者)と、参加者全員に対して、メールを送信します。

メールの送信タイミングは予約したタイミングと、ミーティング開始10分前、ミーティング終了10分前とします。


まずは適当に雑なコードを書いてみます。

// ドメイン層
class UserId(val value: Int) extends AnyVal
class Email(val value: String) extends AnyVal

class User(val id: UserId, val email: Email)

trait UserRepository:
  def getByIds(ids: List[UserId]) =
    ids.map(User(_, Email("hoge@example.com")))

class Meeting(
    organizerId: UserId,
    participantIds: List[UserId],
    startAt: LocalDateTime,
    endAt: LocalDateTime,
    title: String,
    detail: String
)

trait MeetingRepository:
  def save(meeting: Meeting): Unit

// インフラストラクチャ層
class EmailSender:
  def send(to: Email, subject: String, detail: String): Unit =
    println(s"email send to $to")

class EmailReserver:
  def reserve(
      to: Email,
      subject: String,
      detail: String,
      when: LocalDateTime
  ): Unit =
    println(s"email reserved sending to $to when $when")

// サービス層
class MeetingService(
    meetingRepository: MeetingRepository,
    userRepsitory: UserRepository
):
  def reserve(
      organizerId: UserId,
      participantIds: List[UserId],
      startAt: LocalDateTime,
      endAt: LocalDateTime,
      title: String,
      detail: String
  ): Unit =
    val meeting =
      Meeting(organizerId, participantIds, startAt, endAt, title, detail)
    meetingRepository.save(meeting)

    def notify(user: User) =
      EmailSender().send(user.email, title, detail)
      EmailReserver().reserve(user.email, title, detail, startAt)
      EmailReserver().reserve(user.email, title, detail, endAt)

    val users = userRepsitory.getByIds(organizerId :: participantIds)
    users.foreach(notify)


面倒なのでファイル構成とかは考えてません。

あと実際の処理も面倒なので全部printです。

それと、メール送信の予約は実際には開始終了時間の10分前と言いましたが、

本質と関係ない仕様なので一旦無視しています。

まとめ

今回は応用編の初回ということで、例として扱うドメインの要件と最初の実装を行いました。

次回から少しずつリファクタリングしていこうと思います。

この記事をシェアする