
メディカルフォースの(非公式)エンジニア勉強会に潜入してみました
こんにちは、メディカルフォースnote事務局です。メディカルフォースでは有志のエンジニアが非公式に集まって、2ヶ月に1回、技術に関する勉強会をおこなっています。
ここ1年以内に始まった取り組みでまだまだ進化中の企画ですが、より多くの技術を愛する人にも知っていただきたいという思いから、勉強会に潜入してみました。本記事ではその様子を紹介します。
勉強会概要
エンジニア勉強会の目的は技術に関する知識のディスカッションです。非公式なので、明確に目的があるわけではありませんが、有志が集まり、2ヶ月に1回くらいの頻度で開催しています。一回の参加者はだいたい4〜6名ほど。会社の垣根を超えて参加しており、普段使っている言語も制限せずに、広い視野から情報交換をしています。
潜入しました

直近では12月14日に、移転して広くなったメディカルフォースオフィスで開催。11月下旬に移転して以来、オフィスを使用しての勉強会は初の試みでした。
この日のテーマは「DDD」です。社内外の知見を持ち寄り、広い視点からディスカッションすることでDDDに関する普遍的で網羅的な理解を深めたいという想いから設定された本テーマに基づいて、各自、DDDをやっていて気になることを出し合いました。

DDDをテーマとしたものの、その時気になることを中心に自由に発散させていったため、「エンドポイントのインターフェース定義にバックエンドの Value Objectを使うのはアリ?」というものから始まり、「DI Container 」の設計や、ドメイン知識に依存する箇所の処理、エンドポイントのインターフェースをどうやって共有するか、またどこまでの情報を共有するか、PDF化とDLフロントエンドとサーバー接続の話など、次々と出てくる話について参加メンバーでディスカッションし、気がつけば2時間休憩なしで話をしていました。

ここでは、一部をかいつまんで取り上げます。
テーマの一つであった「DI Container 」の概念についてメンバー全員で確認をします。
一部をかいつまんでいくと、以下のように、App が物語全体のスケッチのようなもので、App を初期化する (= 実際の物語にする) ためには、後半の依存関係と実際の実装をみながら順番に登場人物を描いていく必要があるという話などをしました。
— interface DbClient {}
interface RepositoryA {}
interface RepositoryB {}
// some domains
interface UsecaseP {}
interface UsecaseQ {}
interface ControllerX {}
interface ControllerY {}
interface App {}
const dbClientFactory = (): DbClient => ({});
class RepositoryAImpl implements RepositoryA {
constructor(dbClient: DbClient) {} }
class RepositoryBImpl implements RepositoryB {
constructor(dbClient: DbClient) {} }
class UsecasePImpl implements UsecaseP {
constructor(repositoryA: RepositoryA) {} }
class UsecaseQImpl implements UsecaseQ {
constructor(repositoryA: RepositoryA, repositoryB: RepositoryB) {} }
class ControllerXImpl implements ControllerX {
constructor(usecaseP: UsecaseP) {} }
class ControllerYImpl implements ControllerY {
constructor(usecaseQ: UsecaseQ) {} }
class AppImpl implements App {
constructor(controllerX: ControllerX, controllerY: ControllerY) {} }
これ(↑)を初期化したいとすると
// DI Container を使用した時の、基本的な初期化方法
const container = {
DbClient: dbClientFactory,
RepositoryA: RepositoryAImpl, // depends on (dbClient)
RepositoryB: RepositoryBImpl, // depends on (dbClient)
// UsecaseP: UsecasePImpl, // depends on (repositoryA)
UsecaseQ: UsecaseQImpl, // depends on (repositoryA, repositoryB)
ControllerX: ControllerXImpl, // depends on (usecaseP)
ControllerY: ControllerYImpl, // depends on (usecaseQ)
App: AppImpl, // depends on (controllerX, controllerY)};
container.resolve(App) // ↑ 元に 適当に new とかしてくれる
// DI Container を使用しないで new を使って初期化した場合
const dbClient = dbClientFactory()
const repositoryA = new RepositoryAImpl(dbClient)
const repositoryB = new RepositoryBImpl(dbClient)
const usecaseP = new UsecaseP(repositoryA)
const usecaseQ = new UsecaseQ(repositoryA, repositoryB)
const controllerY = new ControllerY(usecaseP)
const controllerX = new ControllerX(usecaseQ)
const app = new AppImpl(controllerX, controllerY)
そもそも new を書きまくらなくていいように DI Container を使用している面はありますが、new の使用が増えても、DI Continer を使用しないことで、各クラスに decorator を書かなくてもよくなり、動的な初期化がなくなるので、ランタイムエラーする前に静的に依存関係の不足に気付けるなど、メリットも多いと思うという話がありました。
エラー処理の話
続いてエラー処理の話に移ります。
Usecase は Domain の上に構成されていますが、Domain でも Usecase でもエラーが発生します。Domain のエラーをそのままフロントエンドに返すと不親切なエラーになりがちです。そのため、なるべく Usecase で Domain エラーを受け取りUsecase の情報を付加して Usecase エラーに変換するのが理想です。ただし、Domain エラーのハンドリング漏れが出る可能性があるということが話題に上がりました。
普段からRustを使っているエンジニアさんからは「 Rust ではエラー処理に Result 型を使っていて、try-catch しないので、エラーの抜け漏れを防ぎやすい」という話が出て、「だからこそ Rust が良いんですよね〜」というRust愛に繋がっていきました。
一方で、メディカルフォースではTypeScriptを採用しています。よって、その場合は一般的には try-catch を使用しますが、Result 型を使用することもできます。しかし、TypeScript の言語仕様レベルでのサポートがないので、「Result 型を安易に導入しない方がいい」みたいな記事も見かけますよねという話も出ました。とはいえ、DDDを採用しているので導入が楽になりそうであり、本当にちゃんとやりたいなら Result 型をTypeScript でも使うメリットは多そう、という結論に帰結しました。
その他、 現状では Domain で英語のエラーメッセージで throw しているのですが、ミドルウェアで変換処理しない限りはそのままフロントエンドに英語で表示されてしまう…という話も盛り上がりました。
ピザと雑談

夕方には、ピザを食べながら雑談タイム。各会社の技術スタックの話や、勉強会のときに出たエラー処理の話の続きなど、話題は尽きませんでした。
(最後に宣伝)勉強会参加メンバー&エンジニアを募集しています
このような形で非公式におこなっているメディカルフォースエンジニアの勉強会。次回もDDDをテーマに、2月15日(土)に開催を予定しております。まだまだ参加募集しておりますので、興味のある方は覗いてみてください。
ちなみに、メディカルフォース社もメンバーを募集しています。勉強会に興味があったりする方は一度カジュアルに話をしてみませんか?