Upgrade to Pro — share decks privately, control downloads, hide ads and more …

スピンオフサービス構築で培われた開発ノウハウをご紹介!

 スピンオフサービス構築で培われた開発ノウハウをご紹介!

AkitoTsukahara

March 13, 2023
Tweet

More Decks by AkitoTsukahara

Other Decks in Programming

Transcript

  1. Copyright© M&Aクラウド
    スピンオフサービス構築で
    培われた開発ノウハウをご紹介!
    PHPerKaigi2023 リジェクトカンファレンス Akito.Tsukahara

    View Slide

  2. Copyright© M&Aクラウド 2
    自己紹介
    塚原彰仁
    AkitoTsukahara
    株式会社M&Aクラウド
    AkitoTsukahara
    akito_tsukahara

    View Slide

  3. Copyright© M&Aクラウド
    昨年2022年11月に
    「資金調達クラウド」を
    リリースしました🎉
    3

    View Slide

  4. Copyright© M&Aクラウド
    スピンオフサービス構築で
    培われた開発ノウハウを
    お伝えします!
    4

    View Slide

  5. Copyright© M&Aクラウド
    この発表で聞けることは?
    今日話すこと
    ● スピンオフサービス開発で直面した課題と解決策
    ○ 「サブドメイン」か?「パス」か?後回しにできない仕様決定
    ○ 同じアカウントでPFを行き来できるようにする
    ○ 新しいサービスドメインを確立させていくには
    今日話さないこと
    ● プロジェクトマネジメントに関するノウハウ

    View Slide

  6. Copyright© M&Aクラウド
    ざっくりとサービスの紹介
    ● M&Aクラウド
    ○ M&Aクラウドは会社・事業を売却したい売り手と会社・事業を買収したい買い手のマッチン
    グプラットフォーム
    ○ 資金調達することもでき、事業会社からの調達が可能

    View Slide

  7. Copyright© M&Aクラウド 7
    ざっくりとサービスの紹介
    求人広告のように買い手がM&Aニーズを公開することで、売り手自ら買い手を探せる
    ダイレクトマッチングにより人に依存しないスケーラビリティを実現
    買い手
    月額無料
    成功報酬
    ・ 案件報酬はM&A成功報酬のみ
    ・最低手数料なし
    ・売り手ソーシングのチャネルが増える
    ・M&Aニーズを発信するだけで売り手を集客
    ・仲介業者を介さずにダイレクトにやり取り可能
    ・潜在層にもリーチが可能
    手数料無料
    売り手
    ・買い手のM&Aニーズを自ら調べることが可能
    ・仲介業者を介さずにダイレクトにやり取り可能
    ・買い手のM&A担当者に直接コンタクトをとれる
    ・仲介業者を使わないので手数料が無料
    買い手のメリット 売り手のメリット
    ・仲介手数料が無料
    〇〇領域の会社を
    募集します
    この会社と一緒に
    やっていきたい!
    1.掲載する 2.オファー
    M&A・出資ニーズを掲載

    View Slide

  8. Copyright© M&Aクラウド
    新サービス「資金調達クラウド」
    ● 資金調達クラウド
    ○ 資金調達目的で登録してくださるユーザーが増加傾向にある
    ○ M&Aクラウドで資金調達ができることの知名度が低い
    ■ 資金調達ユーザー向けのプラットフォームを開発しよう

    View Slide

  9. Copyright© M&Aクラウド
    出資企業 資金調達企業
    9
    資金調達クラウドのサービスフロー
    完全無料で出資企業のマッチング(紹介)ができるプラットフォーム
    手数料完全無料
    1.掲載する
    3.資金調達依頼
    M&A・出資ニーズを掲載
    2.発信する
    〇〇領域の会社に
    出資したい!
    この会社から
    資金調達を受けたい
    【メリット】
    出資ニーズを発信するだけで、資
    金調達ユーザーから連絡が来る
    【メリット】
    出資企業の出資ニーズを見ること
    で、適切なマッチングが可能

    View Slide

  10. Copyright© M&Aクラウド
    リリースまでの大まかな流れ
    ● 開発スケジュール
    ○ PdMとビジネスサイドのすり合わせは3ヶ月程度
    ○ 開発は3ヶ月(7月〜10月末)
    ○ リリースは11月1日(必達で!)
    ● スコープ
    ○ 新規作成は新プラットフォームの3ページ
    ■ リリースに間に合うように調整可能
    ○ 既存プラットフォームと共存できるように機能拡張
    ■ 同じアカウントで資金調達とM&Aの両プラットフォームを利用したい

    View Slide

  11. Copyright© M&Aクラウド
    リリースまでの大まかな流れ

    View Slide

  12. Copyright© M&Aクラウド
    開発における課題と解決策
    12

    View Slide

  13. Copyright© M&Aクラウド
    ● サイト構成
    ○ 「サブドメイン」と「パス」どっちで新サービスを用意するのか
    ■ この選択で開発作業が大きく変化する
    ■ finance.macloud.jpOR macloud.jp/finance
    ● 同じアカウントでどちらのサービスも利用できる
    ○ M&A目的でも後から資金調達に切り替えられるように(その逆も)
    ● 新しいサービスドメインを確立させていく
    ○ 既存サービスドメインと中途半端に混ざらないように
    開発における課題

    View Slide

  14. Copyright© M&Aクラウド
    ● サイト構成
    ○ 「サブドメイン」と「パス」どっちで新サービスを用意するのか
    ■ この選択で開発作業が大きく変化する
    ■ finance.macloud.jpOR macloud.jp/finance
    ● 同じアカウントでどちらのサービスも利用できる
    ○ M&A目的でも後から資金調達に切り替えられるように(その逆も)
    ● 新しいサービスドメインを確立させていく
    ○ 既存サービスドメインと中途半端に混ざらないように
    開発における課題

    View Slide

  15. Copyright© M&Aクラウド
    (課題・なぜ)サイト構成
    ● 新しいサービスを「サブドメイン」と「パス」のどちらでリリースするか問題
    ○ サービスの関連性
    ○ ユーザーの認識性
    ○ SEOの影響
    この判断はエンジニアチームではなく、経営サイドの判断が必要になる
    将来的にプラットフォームをどのように拡大させていくのか、事業構想に直結する

    View Slide

  16. Copyright© M&Aクラウド
    ● 後回しにできない仕様決定である
    ○ サブドメインなら
    ■ サブドメインによるログインセッション管理
    ● 同じアカウントでPFを行き来するため
    ■ 既存フロントフレームワークへのページ追加だと対応が複雑に(Nuxt.js)
    ● できなくはないが、基本仕様から離れた実装になる
    ● 新しくフロントエンド環境を用意することも検討
    ○ 新しいデプロイフローが必要になる
    弊社は「サブドメイン」にする決定をしたので、サブドメイン採用時の対応内容をまとめていきます!
    (課題・なぜ)サイト構成

    View Slide

  17. Copyright© M&Aクラウド
    (課題・解決策)サイト構成
    サブドメインによるログインセッション管理方法(Laravel)
    値をしてしないとnullになるが、
    とすることでサブドメインのセッション共通化が行えるようになる
    'domain' => env('SESSION_DOMAIN', null),
    config/session.php
    APP_NAME=macloud.jp #一緒に変更する必要あり
    SESSION_DOMAIN=.macloud.jp
    .env

    View Slide

  18. Copyright© M&Aクラウド
    (課題・解決策)サイト構成
    この対応でハマったポイント
    すでにログイン中のユーザーは古いドメイン(サブドメインじゃない)のCookieを保持しているため、
    ログインセッションが狂ってしまう。(Cookie名の重複)
    そのためCookie名を変更する必要がある。その際にログイン中のユーザーは強制ログアウトされてしま
    うので注意!
    'cookie' => env('SESSION_COOKIE',
    str_slug(env('APP_NAME', 'laravel'), '_').'_session'),
    config/session.php
    APP_NAME=macloud.jp
    .env

    View Slide

  19. Copyright© M&Aクラウド
    (課題・なぜ)サイト構成
    ● 既存フロントフレームワークへのページ追加だと対応が複雑に(Nuxt.js)
    ● もしも、どちらのサービスにも同じパスが必要になった場合
    ○ macloud.jp/offers
    ○ finance.macloud.jp/offers
    ● pages配下にoffersディレクトリは1つしか置けないため、実装できない...
    → /about
    →/
    Nuxt.jsでは/pagesフォルダ構
    成によってルーティングが決
    まります。

    View Slide

  20. Copyright© M&Aクラウド
    (課題・なぜ)サイト構成
    ● 既存フロントフレームワークへのページ追加だと対応が複雑に(Nuxt.js)
    ○ サブドメインの場合
    ■ 基本のルーティング仕様から離れた実装が必要になる
    ■ ディレクトリを分けて、モジュールでルーティング機能を拡張する
    ● nuxt-router-module で実装できそうな見込み
    ○ パスの場合
    ■ /AAA/mypage, /BBB/mypageとパスを分けるだけで対応できる

    View Slide

  21. Copyright© M&Aクラウド
    (課題・なぜ)サイト構成
    ● 別の課題としては、
    ○ ビルドが遅い
    ○ 一方のチームとコンフリクトする確率が高い
    ○ FeatureFlagの切り替えが増えるほど、if分岐が増えて実装が面倒になる
    ■ コードの可読性が下がる
    ■ リリース後にコードを削除する手間が増える

    View Slide

  22. Copyright© M&Aクラウド
    (課題・解決策)サイト構成
    ● せっかくならフロントフレームワークを新しく用意しよう!
    ○ SSRできること
    ○ 素早くキャッチアップできること
    ○ 将来性のあるもの(?)
    →大事なのは、チームにあったものフレームワークを選択する

    View Slide

  23. Copyright© M&Aクラウド
    (課題・解決策)サイト構成
    詳しくはこちら!

    View Slide

  24. Copyright© M&Aクラウド
    ● サイト構成
    ○ 「サブドメイン」と「パス」どっちで新サービスを用意するのか
    ■ この選択で開発作業が大きく変化する
    ■ finance.macloud.jp OR macloud.jp/finance
    ● 同じアカウントでどちらのサービスも利用できる
    ○ M&A目的でも後から資金調達に切り替えられるように(その逆も)
    ● 新しいサービスドメインを確立させていく
    ○ 既存サービスドメインと中途半端に混ざらないように
    開発における課題

    View Slide

  25. Copyright© M&Aクラウド
    (課題・なぜ)同じアカウントでどちらかのサービスを利用できる
    ● 売却ユーザーは「M&Aクラウド」、調達ユーザーは「資金調達クラウド」
    ○ 売却⇆調達で目的が変わっても同じアカウントで実施したい
    ○ 調達して、事業を拡大し、最後にM&AでExitできるようにサポートしたい
    ○ ユーザーがどちらのPFを利用しているのか意識することなく、サービスを利用できる
    ようにしたい
    ● 実現させるには
    ○ 新しいログインステータス
    ○ 文言の出し分け

    View Slide

  26. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    ● 新しいログインステータス
    ○ usersテーブルに資金調達ユーザーのフラグ(isFunding)を追加
    ■ 資金調達クラウド経由で登録・変更するとフラグがONに
    ■ 既存の調達ユーザーにもフラグを付与する
    ○ 売り手サービスドメインのエンティティに値オブジェクト(isFunding)を追加することで、
    ログイン中の売り手が調達ユーザーかどうか識別できるように

    View Slide

  27. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    ● 新しいログインステータス
    M&Aクラウド
    ユーザー
    資金調達クラウド
    ユーザー
    isFunding
    ログイン
    true
    false

    View Slide

  28. Copyright© M&Aクラウド
    (課題・なぜ)同じアカウントでどちらかのサービスを利用できる
    ● 文言の出し分け
    ○ ログインユーザーのステータスに合わせて表示を切り替える
    ■ サイトのヘッダー・フッター
    ■ 登録情報項目
    ■ メッセージやメールの内容
    ○ ページ
    ■ (back)middlewareで出し分けするための変数を設定
    ■ (front)データをフェッチしてページ内で文言の出し分け
    ○ メール
    ■ Mailableを拡張してユーザー毎に出し分け

    View Slide

  29. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    ● 文言の出し分け

    View Slide

  30. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    /**
    * @var array|string[]
    * @description ユーザーの資金調達フラグに応じて、資金調達クラウドとして振る舞いたいページを指定する指定はRouteName
    */
    protected array $routeNameCheckedByUser = ['AAAApage','BBBBpage'];
    /**
    * @param Request $request
    */
    public function handle($request, ¥Closure $next)
    {
    if ($this->isIncludeRouteNameCheckedByUser($request)) {
    $user = $this->authUserHelper->getUser();
    if (!is_null($user)) {
    $this->viewFactory->share(
    'serviceAttribute',
    new ServiceAttribute($user->isFunding()->rawValue())
    );
    return $next($request);
    }
    }
    }
    App/Http/Middleware/SetServiceAttribute.php
    一部抜粋した実装イメージ

    View Slide

  31. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    class ServiceAttribute
    {
    public function __construct(private readonly bool $isFunding)
    {
    }
    public function isFunding(): bool
    {
    return $this->isFunding;
    }
    public function getName(): string
    {
    return $this->isFunding ? '資金調達クラウド' : 'M&Aクラウド';
    }
    }
    App/DataTransferObjectServiceAttribute.php
    一部抜粋した実装イメージ

    View Slide

  32. Copyright© M&Aクラウド
    (課題・なぜ)同じアカウントでどちらかのサービスを利用できる
    ● メールの文言の出し分け

    View Slide

  33. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    ● メールの文言の出し分け(サービス毎に情報を出し分けする設計)
    SellerMailable
    BaseSellerMailable
    SellingTargetComplete
    CreationToSeller
    Mailable
    SellerImporta
    ntMailable
    SellerMagazin
    eMailable
    Laravel 売り手向けメールの
    抽象クラス
    売り手向けメールの送信
    元を定義した
    抽象クラス
    売り手ユーザーが案件登録し
    た時に送信するメールのサブ
    クラス

    View Slide

  34. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    abstract class BaseSellerMailable extends BaseMailable
    {
    public function __construct(
    public ServiceAttribute $serviceAttribute,
    ) {
    }
    public function serviceNamePrefixSubject(string $title): static
    {
    return $this->subject("【 {$this->serviceAttribute->getName()}】 {$title}");
    }
    public function serviceNamePostfixSubject(string $title): static
    {
    return $this->subject("{$title}【 {$this->serviceAttribute->getName()}】 ");
    }
    }
    BaseSellerMailable.php
    一部抜粋した実装イメージ
    出力イメージ
    【M&Aクラウド】(メールタイトル)
    【資金調達クラウド】(メールタイトル)

    View Slide

  35. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    abstract class SellerMailable extends BaseSellerMailable
    {
    public function __construct(
    public ServiceAttribute $serviceAttribute,
    ) {
    parent::__construct($serviceAttribute);
    if ($this->serviceAttribute->isFunding()) {
    $this->from(
    config('mail.finance.address'),
    config('mail.finance.name'),
    );
    } else {
    $this->from(
    config('mail.from.address'),
    config('mail.from.name'),
    );
    }
    }
    }
    SellerMailable.php
    サービスに合わせてメールの
    ドメインを変更する
    一部抜粋した実装イメージ

    View Slide

  36. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    class SellingTargetCompleteCreationToSeller extends SellerMailable
    {
    public function __construct(
    public AbstractSellingTarget $sellingTarget,
    public Seller $seller
    ) {
    parent::__construct(new ServiceAttribute($seller->getIsFunding()->rawValue()));
    }
    public function build(): SellerMailable
    {
    $title = $this->serviceAttribute->isFunding() ? '調達情報の入力が完了しました。' : '売却情報の入力が完了し
    ました。';
    return $this
    ->serviceNamePrefixSubject($title)
    ->to($this->seller->getEmail()->rawValue())
    ->markdown('emails.selling_target.complete_creation.to_seller');
    }
    }
    SellingTargetCompleteCreationToSeller.php
    一部抜粋した実装イメージ
    メールタイトル、宛先、メー
    ルテンプレートを用いて生成
    する

    View Slide

  37. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    ● メールの文言の出し分け

    View Slide

  38. Copyright© M&Aクラウド
    (課題・解決策)同じアカウントでどちらかのサービスを利用できる
    この対応でハマったポイント(未来形)
    ● 技術的負債になっていく
    ○ 案件を複数化する機能ニーズが高まってきたタイミングで、
    負債解消としてリファクタリングが必要になる
    ■ パーミッションのような扱いにしてもよかった
    ● パーミッション毎にページを用意する
    ■ ログイン基盤の作成
    売り手情報にisFunding
    を持たせている

    View Slide

  39. Copyright© M&Aクラウド
    ● サイト構成
    ○ 「サブドメイン」と「パス」どっちで新サービスを用意するのか
    ■ この選択で開発作業が大きく変化する
    ■ finance.macloud.jp OR macloud.jp/finance
    ● 同じアカウントでどちらのサービスも利用できる
    ○ M&A目的でも後から資金調達に切り替えられるように(その逆も)
    ● 新しいサービスドメインを確立させていく
    ○ 既存サービスドメインと中途半端に混ざらないように
    開発における課題

    View Slide

  40. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● サービス毎の情報差分を分けてそれぞれサービスドメインで表現する
    ○ 共通部分は継承しつつ、新しい情報は新規サービスドメインで表現
    ■ 出資に関する募集情報を参考に

    View Slide

  41. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● 共通部分は継承しつつ、新しい情報は新規サービスドメインで表現
    ○ サービスを分けることでサービスドメインの重要な情報が変わる
    ○ 既存のサービスドメインで対応するとファットクラスになってしまう
    ■ 2つのサービスで利用するデータを全部持っているクラスが誕生する
    ○ 一方のサービスで機能改修時に思わぬ事故が発生したりもする
    ■ 不具合を発生させないような設計にしたい

    View Slide

  42. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく

    View Slide

  43. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● 共通部分は継承しつつ、新しい情報は新規サービスドメインで表現
    Offer
    募集情報クラス

    View Slide

  44. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● 共通部分は継承しつつ、新しい情報は新規サービスドメインで表現
    BaseOffer
    抽象募集クラス
    AcquisitionOffer
    InvestmentOffer
    買収募集クラス
    出資募集クラス

    View Slide

  45. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● 影響範囲があまりも広かったため、一時的に
    Offer
    募集クラス
    InvestmentOffer
    出資募集クラス

    View Slide

  46. Copyright© M&Aクラウド
    (課題・なぜ)新しいサービスドメインを確立させていく
    ● 影響範囲があまりも広かったため、一時的に
    ○ これも技術的負債をいつ返すのかというお話し
    ■ 1. 作ったらすぐに返済する
    ■ 2. 次の改修前に返済する
    ● 普段の業務効率を著しく悪くするなら1のパターン
    ● そうでなければ2のパターン

    View Slide

  47. Copyright© M&Aクラウド
    ● 技術的に負債について
    ○ 財務的に利息と元本が投資の利回りよりも低ければ、負債はポジティブ
    ○ ソフトウェアでも同じことが当てはまるはず
    開発における課題と解決策
    最初に市場へ出るために内部品質を犠牲にしたとしても、より良い内部品質で後から市場に出た
    場合よりも、この決定によって得た金額が高ければ、利益を生み出します。
    しかし、ある程度不確かなことがあるため、そのような利益を予め見積もるのは難しいので、こ
    こにはリスクがあると言えます。
    引用:InfoQ(技術的負債を管理する)

    View Slide

  48. Copyright© M&Aクラウド
    本日のおさらい
    48

    View Slide

  49. Copyright© M&Aクラウド
    まとめ
    ● 「サブドメイン」か?「パス」か?
    ○ 両方に対してのメリット・デメリットを用意する
    ● 同じアカウントでPFを行き来できるようにする
    ○ 「サブドメイン」か「パス」かで難易度が変わってくる
    ○ 「サブドメイン」は対応できるのか、できるとしてどれくらいかかるのか調査
    ○ 既存Viewの出し分けは、多少の辛さを覚悟しておく(メール大変だった。。。)
    ● 新しいサービスドメインを確立させていくには
    ○ クラス設計においては、適宜分けていく
    ● 技術的負債と投資のバランス、負債の返済タイミング

    View Slide

  50. Copyright© M&Aクラウド
    参考資料
    ● https://tech.macloud.jp/entry/2022/08/30/122131
    ● https://ymmooot.dev/articles/3/
    ● https://www.infoq.com/jp/articles/managing-technical-debt/

    View Slide

  51. Copyright© M&Aクラウド
    ありがとうございました!
    51

    View Slide