【テックブログ】新卒エンジニアが入社2ヶ月で新規サービスをリリースした話。

こんにちは! 株式会社Hajimari22卒エンジニアの神野 凌太郎です。

普段は、事業部づけのエンジニアとして人事プロパートナーズの開発業務を担当しています。 内定者インターン〜新卒入社後の期間、人事プロパートナーズの開発と並行して 新規サービス「アミーチ」の開発・立ち上げを1人で行いました。

今回は、実際に新規事業の開発・立ち上げを行なって、学んだこと・反省点をまとめてお伝えできればと思います!

■新規サービス「アミーチ」概要

『人事専門型の求人サイト』として立ち上げたサービスです。 もともと、人事プロパートナーズ内で人材紹介を行なっており、 その専用サイトを立ち上げようというところからスタートしています。

人事を扱う求人サイトだからこそ、学校の保健室のように、 気軽にキャリアを考えられる、そんな求人サイトを作りたいという思いから、 「アミーチ」の立ち上げがスタートしました。

現在、リリースから約2ヶ月たち、 ユーザー数、求人数ともに右肩上がりで増えていますので、 正社員人事を採用したい企業様、人事職にチャレンジしたい、キャリアアップをしたいとお考えの求職者様、ぜひご登録をお待ちしております!

biz.ami-chi.com

ami-chi.com

■開発に着手するまで

開発をするにあたって、まずざっくりとした要件定義を行いました。 後述しますが、当時は何よりも早く開発することを優先していたため、 開発の段階では「ざっくりと要件を詰めれば良い」という認識でいました。

サイトに関しては、先に求人を募るために TO B → TO Cサイトの順でリリースを目指すことになりました。

具体的なスケジュールとしては、 TO B向けサイトは5月末、TO C向けサイトは6月中旬のリリースを目指しました。

技術選定は、Laravelとvue.jsを利用し、デザイン部分はbootstrapを利用することにしました。

サービスの立ち上げ・開発自体が初めての経験であったかつ、 1人で開発を行う(LPは外注しました)という状況だったので、 不安を抱えながら開発をすることになりました。

■開発過程〜リリースに至るまで

実際、TO B向けサイトは5月末、TO C向けサイトは6月中旬と スケジュール通りにリリースまで持っていくことができました。 しかし、リリースに至るまでいくつものハードルがありました。

要件が途中でいくつも変更があったこと

最も大変だったハードルは、「途中で要件が変わること」でした。

もともとフワッと始まったプロジェクトだったので、 要件をガチガチに決めていなかったこともあり、ビジネスサイドとの認識齟齬がある状態で 開発を進めてしまったためです。

特に、リリース直前にいくつか改善案や意見をもらったときは、 「本当にリリースできるのか?」と思ったくらい絶望的な状態でした。

ただ、もともとひいていたスケジュールより、 前倒しして開発を進めていたので、優先順位をつけて対応することで リリース時期がズレることなく、リリースすることができました。

学んだこととしては、

  • 時間がかかっても、要件定義はガチガチに固めてから開発をすること
  • キャパを越えそうなときは、優先順位を目に見える形で洗い出してから対応すること

です。

要件定義は、開発スピードを優先していたとしても、 きちんと行なったほうがスムーズに開発できることを身を持って知りました。(当たり前のことかもしれませんが) 最低でも、「テーブル定義」「機能設計」「画面設計」「URI設計」「モデル図」くらいは行なったほうが良いと思います。 アウトプットをあらかじめ行なっておくことで、プロジェクトメンバーとコンセンサスが取れている状態で認識齟齬なく開発を行うことができます。

メインサービス「人事プロ」との開発両立

2つ目は、「サービス開発の両立」です。 弊事業部では、エンジニア2人チームで行っているため、 新規サービスの開発だけに専念できるわけではありません。

別で、先輩エンジニアが大規模の新規サービス開発を行なっていたため、 そちらの開発にかかっきりな状態ということもあり、 メインサービスの「人事プロ」の開発業務も兼任していました。

新規サービスを立ち上げをするにあたって、 大前提メインの事業の収支を成り立たせないといけません。

また事業自体が少数精鋭で運営しているので、 エンジニア観点から常にインパクトのある改善、開発を行なっていく必要があります。

新規プロダクトの開発は楽しいし、やりがいもある一方で、 会社や事業部の誰かがメインの事業にコミットして数字を成り立たせてくれるからこそ、 チャレンジができます。

両立はすごく大変でしたが、改めて数字を成り立たせる重要性を身を持って体感しました。

不安との葛藤

1人で開発していたということもあり、常に不安との葛藤がありました。 特に、技術が成熟しているわけでもなく、「このコード設計で問題なく動くのかな…」といった不安は常につきまとっていました。

加えて、リソースもギリギリな状態で開発を行なっていたので、 納期へのプレッシャーもたくさんありました。

不安を解消するために行なったことは、 開発後の「セルフレビュー」と「テスト」です。

自分が書いたコードが動くわけがないという視点で セルフレビューとテストを行うことによって、サービスのクオリティを維持することができました。

学んだこととしては、開発をひと段落した後に安堵、過信をしないこと。 繰り返し確認・テストすることで、サービスクオリティ維持、不安の解消に繋がることを身を持って知ることができました。

■まとめ

新卒入社でいきなり新規サービスの開発を任される経験は滅多にないと思うので、 かなり良い経験でした!

実際に開発が終わり、リリースした時の感動は凄まじかった…!

まだサービスの改善をするところはたくさんあるので、 より良いサービスを目指して、日々開発に勤しもうと思います!

最後まで読んでいただきありがとうございました!


株式会社Hajimariでは、Laravelをメイン言語として自社開発・受託開発を行なっており、
一緒に開発を行なっていただけるエンジニア募集しています! 長野拠点の立ち上げメンバーも大募集しています!

興味のある方は以下の記事をぜひご覧ください!
みなさまとお会いできるのを心からお待ちしております!

www.wantedly.com www.wantedly.com www.wantedly.com

puppeteerでbootstrap導入時の影響調査をやってみた!

puppeteerを使ってみた!

こんにちは!
6月から23卒内定者インターン生として株式会社Hajimariに入社した、江端 凌です。

普段は、TUKURÜS事業部で受託開発の業務に携わっています。

今回は受託開発している既存サイトに、bootstrapを導入することになったので、どのような影響が出るのかを調査することになりました。

背景としては、既存サイトのCSSの複雑化と適切なブレークポイントの設置が出来ていなかったことから導入に至りました。

そこでbootstrapの導入前と導入後を比較するために、node.js製のライブラリであるpuppeteerを利用して全ページのスクリーンショットと差分画像を生成しました!

今回はpuppeteerの概要から、実装までの流れを紹介させていただきたいと思います!

puppeteerとは?

puppeteerとは、自動でブラウザ操作を行えるnode.jsのライブラリです。

npmでインストールすると、操作するchromiumも一緒についてきます。
puppeteerは、このchromiumでブラウザを操作していきます。

e-words.jp

puppeteerの概要としては、

  1. ヘッドレスで操作できる。
  2. 動的に生成されるページ(SPAなど)でも操作ができる。

といった特徴があります。

qiita.com

puppeteerを使ってやったこと

では、今回puppeteerを使ってやりたいことを確認していきます。

bootstrapを既存のサイトに導入した場合、すでに存在しているCSSと競合してデザイン崩れを起こす可能性があります。

その時、どのような影響が出るのかを調査したいため、

  • テスト環境(bootstrap未実装)
  • local環境(bootstrap導入済み)

の二つを利用して、puppeteerを使って両方の環境でスクリーンショットを撮りました。

その後、looks-sameというライブラリで差分画像を生成をします。

looks-sameもnode.jsのライブラリですので、puppeteerと一緒にインストールできますが、長くなるので今回は割愛させていただきます。

例として今回は、弊社のホームページのスクリーンショットを自動で撮影するプログラムを作りましょう!

puppeteerを導入してみる

では早速、npmを使ってpuppeteerをインストールしていきます!

node.jsをインストールをしていない場合は、node.jsのインストールを行なってください。

node.jsがインストールされているかどうか*1は、node -vコマンドで確認できます。

`node -v`コマンドの実行画面

qiita.com

npmでインストールする

まず、puppeteerを使うフォルダを作ります。

適当にデスクトップなど(どこでもいいです!)に移動して、以下のコマンドを実行してください。

$ mkdir puppeteer && cd puppeteer

そうしたらフォルダが作成されていると思います。

続いて、npmでpuppeteerをインストールしていきます!

$ npm init && npm install puppeteer

そうするとREPLで色々聞かれるので、全部Enterキーで答えましょう。

使うバージョンなどを聞かれています。

npm init && npm install puppeteerの実行

画像のような表示になったらインストールは完了です。

puppeteer単体をインストールしたい方は、

$ npm install puppeteer-core

で単体でインストールすることが可能です。*2

実際にコードを書いてみる

今回は弊社のホームページスクリーンショットしてみます。

先ほど作成したpuppeteerフォルダ配下にindex.jsと画像を保存するdistフォルダを作成しましょう。

$ touch index.js && mkdir dist

このindex.jsの中に処理を書いていきます。

index.jsファイルがエントリーポイントになります。

e-words.jp

本当は全ページ撮影するなら、ConstsファイルにURLやログイン情報などを保存しておく方が便利ですが、今回は簡易的にindex.jsだけで書いていきます。

index.js

const puppeteer = require('puppeteer');

/**
 * 引数に渡した数 * ミリ秒待機
 * @param {int} msec
 * @returns setTimeout
 */
function sleep(msec) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve();
    }, msec);
  });
}

// puppeteerの設定
(async () => {
  const browser = await puppeteer.launch({
    // ヘッドレスかどうか
    headless: false,  
    // 10000ミリ秒応答が無かったら終了する
    timeout: 10000,  
  });

  const page = await browser.newPage();
  await page.setViewport({ width: 1920, height: 1080 });

  // ページに移動
  await page.goto('https://www.hajimari.inc/');

  // 画面遷移のアニメーションが終わるまで待機
  await sleep(8000);

  // スクリーンショットを取ってdistフォルダに保存する。
  await page.screenshot({ path: 'dist/hajimari.png', fullPage: false });

  // ブラウザを閉じる
  await browser.close();
})();

上記のコードを書いて保存したら、node index.jsをターミナルで実行しましょう。

$ node index.js

distフォルダ配下にhajimari.pngが保存されているはずです。

こんな感じで ↓

Hajimariのトップページ

それぞれのコードについて

それぞれのコードを見ていきましょう。

puppeteerは基本的に非同期で動作するので、asyncawaitで処理を書いていきます。

qiita.com

puppeteerの設定

(async () => {
  const browser = await puppeteer.launch({
    headless: false,  
    timeout: 10000,  
  });

  const page = await browser.newPage();
  await page.setViewport({ width: 1920, height: 1080 });

ここでは、puppeteerの設定ができます。

3行目のheadless: falseは、ヘッドレスにするかどうかを決めています。

今回はわかりやすく、falseにすることでchromiumの動きを見えるようにしました。必要なければtrueにすることで見えなくなります。

8行目で表示するサイズを変えられます。

await page.setViewport({ width: 1920, height: 1080 });

今回はPC表示にしていますが、スマホサイズで撮影したい場合は、
widthheightスマホのサイズにすることで撮影可能です。

スクリーンショットの処理

// ページに移動
await page.goto('https://www.hajimari.inc/');

// 画面遷移のアニメーションが終わるまで待機
await sleep(8000);

// スクリーンショットを取ってdistフォルダに保存する。
await page.screenshot({ path: 'dist/hajimari.png', fullPage: false });

// ブラウザを閉じる
await browser.close();

1行目で目的のページに移動します。

page.goto()に目的のURLを渡してあげましょう。ここを変更すると別のページにも飛ぶことができます。

2行目のsleep()は上の方で作った関数です。

function sleep(msec) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve();
    }, msec);
  });
}

細かい説明は省きますが、引数に渡した数字 × ミリ秒だけ待機します。

なので、await sleep(8000);は8秒待機させています。

3行目の処理はスクリーンショットをして、引数に渡した保存先に保存しています。

4行目でブラウザを閉じて、puppeteerの処理を終了しています。

まとめ

自分はこのpuppeteerアプリを作成したことで、何度も確認したりスマホ画面に切り替えたりできて効率化につながりました!

またヘッドレスでも動くので、puppeteerを動かしながら別の業務もできます。

今回は紹介しきれませんでしたが、差分画像を生成するlooks-sameというライブラリを使ったり、ベーシック認証を突破したり、Xpathで要素を取得してボタンを押したりもできたりして、とても幅広いです。

今後もE2Eテスト用に使ったりできそうなので、より良いものにできるように保守していきたいと思っています!


株式会社Hajimariでは、Laravelをメイン言語として自社開発・受託開発を行なっており、
一緒に開発を行なっていただけるエンジニア募集しています! 長野拠点の立ち上げメンバーも大募集しています!

興味のある方は以下の記事をぜひご覧ください!
みなさまとお会いできるのを心からお待ちしております!

www.wantedly.com www.wantedly.com www.wantedly.com

*1:puppeteer@3.0.0以降は、Node v10.18.1以降の環境が必要になるので、適宜アップデートしてください。

*2:puppeteer単体でインストールする場合は、chromiumはインストールされないので、注意してください。

PHPStan導入のすすめ

こんにちは!
株式会社Hajimari21卒エンジニアの古田 鏡です。

普段は、TUKURUS事業部(旧PIECE事業部)で
自社プロダクトであるスタートアップ向けマッチングサイト構築パッケージPIECE
https://crowd.itpropartners.com/piece/)の開発や受託開発を行っています!
※2022年4月から事業部の方針変更により事業名が変更となりました!

crowd.itpropartners.com

現在ジョインさせていただいている案件では、
組織のオンボーディングシステムの新規機能開発・システム統合等を担当させていただいております。

開発言語に関してはバックエンドでPHPフレームワークはLaravel)を使っていて、
静的解析ツールPHPStanを導入しているのですが、
このツールが便利だったので今回はご紹介していきたいと思います!

PHPの特徴

そもそもPHPは「動的型付け言語」に分類され、
基本的にはデータ型の宣言がいらないプログラミング言語です。

※参考 動的言語と静的言語の違い

型の宣言がいらないことで得られるメリットとしては、

  • どのような型の値でも代入でき、型(文字列・数値・配列・オブジェクト等)を意識する必要がない
  • (型の宣言がいらない分)記述量が少なくすむ
  • ソースコードの変更(修正)に強い

この辺が挙げられるのではないかと思います。

私もこれまで実際PHPの案件をやっている中で、
型を意識せずに面倒から解放される点でPHP最高ー!!と思っていました。

ですが、現在携わっている案件で、複数人での開発・システム統合をする中で、
コードの統合・不要コード削除に伴い、影響範囲の大きな修正が増えたことで、
静的解析の重要性を身に染みて感じました、、、
(修正がバグを生む可能性大、、、テストコードを全て書くのは時間がかかりますし、、、)

そこでPHPStanの出番です!!

●PHPStanとは?

PHPStan は、
PHP コードの静的解析ツールです。 (PHP Static Analysis Toolの略)
https://phpstan.org/

MIT ライセンスで公開されており、
Composer でインストールして利用できます。
また、Docker イメージも公式で公開されております。

github.com

phpstan.org

概要
  • 動作にはPHP 7.2以降が必要
  • 未定義の変数・メソッド・Class・プロパティなどを検出
  • PHPDocの構文チェックが可能

●ルールレベル設定

PHPStanはルールレベル設定があり、
レベルは0~9の10段階で設定できます!
レベル9ともなると結構厳しめのチェックとなるようです、、、!

phpstan.org

Lev 内容
0 基本的なチェック、未知のクラス、未知の関数、$this上で呼び出された未知のメソッド、それらのメソッドや関数に渡された引数の数が間違っている、常に未定義の変数をチェック
1 未定義の変数、call と get を持つクラスの未知のマジックメソッドとプロパティがある可能性がある
2 ($this だけでなく)すべての式で未知のメソッドをチェックし、PHPDocs を検証する
3 戻り値の型、プロパティに割り当てられた型の確認
4 基本的なデッドコードチェック - instanceofやその他の型チェックが常にfalse、到達しないelse文、return後の到達不能コードなど
5 メソッドや関数に渡される引数の型チェック
6 タイプヒントの欠落を報告する
7 部分的に間違っている論理和型の報告 - 論理和型の一部の型にしか存在しないメソッドを呼び出した場合、レベル7はそのことを報告し始めます(その他の不正確な状況も)
8 null可能な型に対するメソッド呼び出しとプロパティへのアクセスを報告する
9 混合型に厳密であること - この型で唯一許される操作は、この型を別の混合型に渡すことである

下記に例題が載っているのでチェックしてみてください!

phpstan.org

●インストール方法

インストールする方法としては

phpstan.org

公式にインストールする方法が記載してあるので、それに従いインストールします。

Composerを使い下記のコマンドを実行します。

composer require --dev phpstan/phpstan

基本的に開発環境で使用するものだと思いますので、
--devオプションをつけるのが良いと思います。

インストールできているか確認

./vendor/bin/phpstan analyse --version
PHPStan - PHP Static Analysis Tool 0.12.99⇦インストールされているバージョンを表示

●実行方法

実行コマンド
./vendor/bin/phpstan analyse

ルールレベルを変更する場合には、 レベルを変更するには --level (-l )オプションで実行します。
↓ではレベル4で設定しています。

.\vendor\bin\phpstan analyse -l 4
設定ファイル(phpstan.neon 一部抜粋)
includes:
    - ./vendor/nunomaduro/larastan/extension.neon

parameters:

    paths:
        - app

    level: 4
実行結果

エラーありの場合

エラーなしの場合

こんな感じで結果が出ます。

●実際に使ってみて得られたこと

  • 導入のしやすさ
    基本的に公式通りでインストールでき、導入のハードルが低いこと。

  • 解析が高速にできる
    状況に応じて、ルールレベル・実行範囲を変えられ、必要部分に対して解析が高速にできること

  • レビューの負担軽減
    システム統合など大きな改修を行う場合、コードレビューの量が多くなりがちですが、
    事前に静的解析を行うことでレビューの量を減らせること
    (個人的にはこの効果が一番大きいと思いました。)

  • ある程度システムの安全性・整合性を担保できる
    型レベルの安全性・整合性が保たれること
    しかしながら、無限ループや関数の型がmixed・null許容の場合、解析をすり抜けてしまうことがあること

●まとめ

大前提として、別途ユニットテスト等は必要だと思いますが
導入と実行の容易さ、時間短縮の観点から非常にコスパの良いツールだと思いました!
プロジェクトが大きくなればなるほど、効果を発揮するツールだと思いますので、是非導入してみてはいかがでしょうか!


株式会社Hajimariでは、Laravelをメイン言語として自社開発・受託開発を行なっており、
一緒に開発を行なっていただけるエンジニア募集しています! 長野拠点の立ち上げメンバーも大募集しています!

興味のある方は以下の記事をぜひご覧ください!
みなさまとお会いできるのを心からお待ちしております!

www.wantedly.com www.wantedly.com www.wantedly.com

Notionで新卒採用管理システムを作ってみた!

こんにちは!
4月に入社した、株式会社Hajimari22卒エンジニアの神野 凌太郎です。

普段は、人事プロパートナーズの開発業務や事業部内でリリースする新規サービスの開発業務を担当しています。

本業はエンジニアですが、内定者時代から23卒エンジニア採用の兼務をしていて、
約1年前、23卒採用が本格的に始まる前にNotionで新卒採用管理システムを作成しました。

今回は、実際に1年間運用してみてよかったことや、改善できたなと思うことを まとめてお伝えできればと思います!

■そもそもNotionとは

www.notion.so

Notionは、ドキュメントやデータベース、TODO管理など
あらゆる情報やデータを1つに集約して管理できるSaaS型のツールです。

弊社では、Notionを社内wikiとして全社で導入しています。
他にも各事業部で、チームのタスク管理やTODO管理で活用していたり、
採用募集のページを公開したりしています。

また約半年前に、弊社のイベントスペースで
NotionTokyoのオフラインイベントが行われたりもしました!

https://notiontokyo14.splashthat.com

■Notionを導入した経緯

Notionシステムを導入する前は、 Slackでチャンネルを作成し、そのチャンネルで候補者ごとにスレッドを立てて申し送りや引き継ぎが行われていました。

課題

Slackを使って申し送りを行うことで、スピーディーに情報共有ができる一方で、 下記の問題点がありました。

  • 候補者の情報を見るために、過去のスレッドを辿る必要がある(ピーク時にはすぐに上に流れていってしまうので、スレッドを見つけるのは困難です)
  • ステータス管理を別でスプシで行っていたが、 更新漏れがたびたび発生しており、リアルタイムでのステータス管理ができていなかった
  • 情報が点在しているため、データ分析をすることが難しく定性的な判断で採用活動を行なっていた

実装する手間を最小限にリアルタイムにデータを集約しようということで、 Notionで新卒採用管理システムを作成することにしました。

■全体の流れ

Notionを使った一連のフローを下記の図にまとめました。
Notionを使うのは、23卒採用メンバー + 面接を担当する社員です。

初回接触の場合は、学生テーブルにレコードを追加し、
基本情報(名前や次回面接日)や関連情報(大学、応募経由など)を記入します。

面接後は評価や所感を記入し、社内の専用Slackに情報共有、エージェントや学生に結果連絡を行います。

2回目以降の面接に関しては、基本的に23卒採用メンバー以外の社員が担当します。
担当社員は、面接終了後にNotionのステータスの更新、結果・所感の記入を行い、
リクルーター(該当学生を担当している23卒採用メンバー)にSlackで連絡を行います。

Notionを活用したときの一連のフロー

■テーブル構成

テーブル構成は以下の通りです。 学生テーブルを中心として、それぞれ「採用メンバー」「大学」「エージェント」「リクルーター」のテーブルと連携しています。「リクルーター」は23卒採用メンバーを指し、 「採用メンバー」は2次面接以降を担当する社員を指します。

Notionのリレーション機能を使った実装を行い、リレーションを利用したのはそれぞれのテーブル別でデータを蓄積するためです。

テーブル構成

■作成にあたって意識したこと

大前提として、23卒採用メンバーにNotionを活用してもらわなければ意味がありません。 導入目的は、リアルタイムにステータス、情報更新してもらうことだからです。

ゴールは、メンバーに「使ってもらえるかどうか」。 そのため、多少データベースの構成が複雑になっても、UIを優先しました。

トップページの親しみ

メンバーはかなりの頻度でNotionを開くので、トップページをこだわろうと思いました。 特にカバー画像は目に優しく、可愛くしようと思っていた時に、たまたま近くに同期の女の子がいたので、作成を依頼しました。めちゃくちゃ可愛い!

トップページ

使い方ページの作成

当時、全社でNotionが導入されたばかりだったこともあり、 Notionの操作に慣れないメンバーがほとんどでした。

また、Notionを使うのは23卒採用メンバーだけではなかったので、
多くの人に等しく操作に慣れてもらう必要がありました。
そこで、使い方ページの作成を行いました。

  • 文字だけではなく動画を使って説明すること
  • リクルーターと面接担当者別のフローに沿って、必要な情報だけ提示すること

意識したことは上記の2つです。
使い方ページを導入したおかげで、
導入障壁があまりない状態で、運用をスタートさせることができました。

記入コストを下げること

冒頭でも記述した通り、ゴールは使ってもらえるかどうかです。
そのため、記入するコストを下げることを意識しました。
具体的には、考えなくても直感的に記述できること。

まず、Notionのテンプレート機能を利用し、
極力労力をかけないで良いように、学生ページのテンプレートを用意しました。

学生ページのテンプレート利用方法
基本情報は、一覧画面で情報の記入や更新ができるようにし、
詳細な情報(学生情報、面談メモ)は学生ページで記述できるようにしました。

見やすさも意識しつつ、記入に思考体力を奪わないように設計を心がけました。

■問題点

実際に1年間運用してみた結果、いくつか問題点が出てきました。

ページが重く、表示速度が遅い

最もクリティカルだったのは、ページが重く、表示速度が遅いことです。
原因は、学生レコードの急激な増加リレーションを貼りすぎたことだと推測できます。

年度の途中で採用予定人数が大幅に増えたことも相まって
学生レコードが増加し、現在700レコード近くデータが入っています。

その上、そのレコードに各関連テーブルのリレーションが貼られている状態なので、
さらにページを重くしています。

学生ページ内、面談評価の面談担当者と採用メンバーテーブルがリレーションを貼っており、特に最も重くしている原因として考えられます。
学生テーブルの学生ページ内でリレーションを貼っているので、採用メンバー全員が面談担当者と紐付いた状態になっているからです。
(しかも、採用メンバーテーブルであまりデータを貯める必要がないことも後からわかったので、リレーションを貼る必要性もありません。)

学生ページ内の面談評価テーブル
この後に、シンプルテーブルの機能がリリースされました。
採用メンバーのテーブルはこれがベストだと感じました。(zoomのリンクやWantedlyの記事の管理は行いたいからです)

www.notion.so

データ集計が難しい

残念ながら、Notionにはスプレッドシートやエクセルのように便利な関数がいくつも用意されていません。
当初はNotion内でデータ分析がリアルタイムに見れる状態を作ろうと思い、
用意されている関数を駆使して、無理やり集計ができるようにしましたが、
あまり活用できていません。

データ分析自体は、スプレッドシートをNotionに埋め込んで同期できるように運用した方が良さそうです。

■まとめ

採用管理システムをNotionで作成したことによって、 データを一元に管理できるようになったので非常に良かったです!
実際に運用してみて問題点が浮き彫りになったので、 24卒以降の管理システムは、より良いシステム構築ができれば良いなと思っています。
1年前に作成した時よりも、機能がかなり充実しているので、 うまく活用していきたいと思います。

Notion大好き!


株式会社Hajimariでは、Laravelをメイン言語として自社開発・受託開発を行なっており、
一緒に開発を行なっていただけるエンジニア募集しています! 長野拠点の立ち上げメンバーも大募集しています!

興味のある方は以下の記事をぜひご覧ください!
みなさまとお会いできるのを心からお待ちしております!

www.wantedly.com www.wantedly.com www.wantedly.com

22卒内定者が『フリーランス人事と案件のマッチングスコア算出』機能をリリースしました!

皆さん初めまして、
22卒内定者で人事プロパートナーズで開発を担当している、神野凌太郎と申します!

今回、人事プロパートナーズ内でフリーランス人事と案件のマッチングスコア算出」機能をリリースしたので、リリースに至った経緯と機能詳細についてご紹介したいと思います!

■人事プロサービス概要

f:id:r_kamino:20211122124847p:plain

各社が抱える組織課題の解決に強みを持った即戦力人事の業務委託人材と
人事のリソース不足でお困りの成長企業をマッチングするサービスです。

興味のある方は、
ホームページからぜひお問い合わせください!

itpropartners.com

■機能開発に至った背景

弊サービスでは、案件をご紹介する前に専属CA(キャリアーエージェント)がご登録いただいたフリーランス人事と面談を行います。

面談では、

  • どういう経験をされてきたか
  • 案件にはどのくらいの日数や、週何時間くらい稼働できるか

などをお聞きして、管理画面に面談情報を登録していきます。

その後、登録した情報をもとにフリーランス人事に案件をご紹介しています。
ただ、弊サービスには多くのフリーランス人事の方にご登録いただいているため、企業担当が案件にマッチするフリーランス人事をピックアップするのが困難です。
そのため、企業担当からCAに案件概要を説明した後、
CAが案件にマッチするフリーランス人事ピックアップしています。

f:id:r_kamino:20211122124958p:plain

問題点としては、

  • 企業担当とCAのコミュニケーションコストが大きいこと
  • CAの属人的な情報で、案件とフリーランス人材のマッチングを行なっていること

が挙げられます。

そこで、「人と案件のマッチングスコア算出」機能をリリースすることで、
少しでも企業担当とCAの工数を削減し、より最適なマッチングを実現しようと考えたわけです。

■マッチングスコア算出のロジック

マッチングスコアは、以下の8項目それぞれの入力内容を突き合わせて算出します。
具体的には、それぞれの項目で0~1の間で評価を行い、設定した重みをかけ合わせて
スコアを算出しています。

f:id:r_kamino:20211122130155p:plain

企業担当とCAのヒアリング結果から、
「月間稼働時間」「勤務形態」「タグ」「稼働可能時間帯」がマッチングにおいて特に大事な項目だということがわかったので、他の項目に比べて重みが大きくなっています。

また、案件・フリーランス人事のどちらか未入力の項目があった場合は、
自動的にその項目は0として評価をします。
項目によっては未入力のほうが入力していた時よりもスコアが大きくなる場合があるからです。

 ■実装手順

 ①仮の評価方法を作成

まず、フリーランス人事と案件の入力項目を見比べて、マッチングに必要な項目を洗い出しました。
項目を洗い出すなかで、どちらか一方にしか項目でも、マッチングに使える項目と判断したものに関しては、新たに項目を追加する想定でピックアップをしました。
ここで、既存のデータにこだわるのではなく、あくまでマッチングに必要な要素は何かという判断軸でピックアップを行うことを意識しました。

②過去データをもとに、手動で評価値を算出

過去のフリーランス人事とその方が稼働している案件情報を用いて、評価値を算出しました。
実際にマッチングが成立した情報を使った時に、評価値に問題がないかを確認するためです。

結果として、全ての項目を0、1だけで評価してしまうと、明らかに他の項目より評価が高くなってしまう項目があったので、それぞれの項目に重みをつける方法を取ることにしました。
データベースに重みの値を保存できるようにして、いつでも値を変えられるようにします。

③CAと企業担当にヒアリング

②までに作成した叩きを用いて、CAと企業担当と要件のアップデートを行いました。
マッチングに用いる項目と重みに問題がないかを確認してもらい、評価方法の改善をするのが目的です。

ヒアリングにおいてCAと企業担当どちらかに偏った意思決定にならないように、
中立に意見を取りまとめるように心がけました。

④入力フォームの設置

要件のアップデートで新たに出てきたマッチング項目を管理画面で登録できるように、入力フォームの設置を行いました。(システム設計上、複数のデータベースと入力ページの改修が必要で、想定以上の工数がかかりかなり大変でした。。)

⑤自動マッチングの実装

マッチングに必要な項目を入力できるようにしたので、最後に自動マッチングの実装に取り掛かりました。
アップデートした要件をもとに、それぞれの項目でスコア算出のコードを書きました。(全部で200行くらいのボリュームです、、)

また、算出したスコアを用いて「フリーランス人事→案件」「案件→フリーランス人事」でマッチング検索できるページを実装しました。

下記の図は、「案件→フリーランス人事」で検索したページの例です。
算出したスコアの高い順でユーザー表示を行うことで、フリーランス人事と案件の最適なマッチングを提案します。

 

f:id:r_kamino:20211122130552p:plain

■開発を通して大変だったこと

最も大変だったことは、自動マッチングの抽象的な要件を具体に落とし込む作業です。

もともと、「フリーランス人事と案件を自動マッチングできたら良いよね」と開発チームでの何気ない会話からスタートしているので、「やりたいことは決まっているけど、具体的には何も決まっていない」状態でした。

そのため、具体的な実装要件を詰める前に、ビジネスロジックをプログラムに置き換える作業が必要です。

まず、スラック上で企業担当とCAがやり取りしているテキストからマッチングに必要な項目を推測し、開発チームで叩きを作成することから始めました。(叩きを作成するのに半日以上かかりました、、)

「叩きがある状態だと効率的に要件を詰めることができる」と上長から教えてもらったという背景から、叩きを先に作成することにしました。

実際に、叩きがある状態で議論をすると、より本質的なマッチング要件を決めることができ、
実装もスムーズに行うことができました。

学んだこととしては、「実装前の要件整理に時間をかけること。」
時間をかけた分だけ、ビジネスに寄り添った機能開発ができることを実感しました。

■まとめ

実際にこの機能をリリースすることで、

  • 月間12.5時間の削減
  • 客観的な情報で誰でも人選が可能な状態

を実現することができました。

実際にCAと企業担当からは、「最適なマッチングが実現できて、リソースが余った分、さらに別のところで価値提供が行えるようになった」というお声をいただきました!

今後、この機能を活用してフリーランス人事の方に直接案件をレコメンドできる機能のリリースも考えています。

他にもまだ活かしきれていないデータは数多くあるので、うまくデータ活用を行なってサービス向上に寄与していきたいと思います!

最後までお読みいただきありがとうございました!

------------------------------------------------------------------------------------

株式会社Hajimariでは、Laravelをメイン言語として自社開発・受託開発を行なっており、一緒に開発を行なっていただけるエンジニア募集しています!

23新卒エンジニアの募集も開始しておりますので、少しでも興味ある方はカジュアルにお話ししましょう!

興味のある方は以下の記事をぜひご覧ください!
みなさまとお会いできるのを心からお待ちしております!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

一泊二日開発合宿!〜思わず移住したくなる森のオフィス〜

こんにちは!

株式会社Hajimari21卒内定者エンジニアの古田です。

今年の6月より内定者インターンとしてHajimariで働いております!

 

普段は、企業と優秀な人事のマッチングサービス

人事プロパートナーズ」の開発業務を行っております!

 

毎日学ぶ事だらけですが、会社の良いメンバーにも恵まれ、

非常に充実した日々を送っております!!!

やっぱり、仕事をする上で誰と働くかが非常に重要だな

最近は、そんなことを実感しております!


さて、今回は社内のエンジニアメンバーと共に一泊二日で開発合宿に行ってきました!

その様子をこのブログで紹介させていただきます!

 

 

開発合宿を行った「富士見 森のオフィス」 

f:id:kyoK:20201109144216p:plain

f:id:kyoK:20201111121752p:plain

(森のオフィス ホームページより)

今回、開発合宿は長野県にある

富士見 森のオフィス」という施設に行ってきました!

 

大自然の中にあるこの施設は、コワーキングスペース宿泊棟が併設されており、

普段はサテライトオフィステレワーク拠点

地域住民の方々にとっての“公民館”的スペースとして使われている施設で、

Hajimariのオフィスがある渋谷からは車で2時間半程のところにあります!

 

 

 

 

コワーキングスペースの様子

f:id:kyoK:20201109145440j:plain

f:id:kyoK:20201109145358j:plain

 

施設内はとても静かで、ストレスを微塵も感じさせない

コワーキングスペースでの作業はめちゃめちゃ集中できました!

少し疲れてきた時にパソコンから目を逸らし、

窓の外に目を向けると辺りには木々が広がっていて癒されました!

開放感が半端なかったです!!

 

施設の外のベンチで作業をしているメンバーもいましたが、

周りに木々の音しか聞こえない中での作業は

都心で働いていては味わう事ができないですし、

そんなストレスフリーな環境がこの施設の一番の魅力だと思いました!

 

 

施設周辺の様子 

f:id:kyoK:20201109144914j:plain

森のオフィスというだけあって、辺りは木々に囲まれていました!

紅葉シーズンだった事もあり、とても綺麗で癒されました!

(僕の語彙力では表現し切れない事が非常に悔しいです、、、)

 

昼食

f:id:kyoK:20201111114316j:plain

f:id:kyoK:20201111114337j:plain

昼食はコワーキングスペースの外にキッチンカーが来ており、

僕はココナッツチキンカレーを頼みました!

美味しかったです、そして健康的!!!

食べ終わった後、

施設内のキッチンで自分の使った食器を洗うと50円引きしてくれます!!!

 

食器洗いをしている時に、

〜日より移住して来ました」という声がちらほら聞こえていたのですが、

これだけ居心地の良い場所なら移住したくなる気持ちが分かるなと思いました。

 

宿泊施設の様子

f:id:kyoK:20201109144846j:plain

f:id:kyoK:20201109163629p:plain

(森のオフィス ホームページより)

 

 

コワーキングスペースの隣にある宿泊施設はとても綺麗でした!!!

今回は、偶然にも他の団体のお客さんがいなかったので貸し切り状態でした!

将来はこんな家に住みたい、、、思わずそう思ってしまいました!

 

f:id:kyoK:20201109163046j:plain

 

夕食は施設の近くにある

生産者直売所の「たてしな自由農村」で

猪や鹿のジビエ、野菜、地酒などを購入し、

宿泊施設のキッチンで調理して食べました!!

 

f:id:kyoK:20201109161807j:plain

特にジビエが最高でした!!!

f:id:kyoK:20201109163033j:plain

 

美味しいご飯を食べながら、

普段はしないような話をできましたし、

よりコミュニケーションが深まりました!!

これもまた、開発合宿の良さなのではないかと思います!

 

 

途中から登山合宿に・・・ 

初日にコワーキングスペースの管理者の方から、

今の時期(11月初旬)は宿泊施設の近くにある

富士見パノラマリゾートという施設のゴンドラで山を登ると

日の出と共に雲海見えるよ!というお話を聞いたので、

メンバー皆で早朝4時に起きて車でゴンドラに乗りに行きました!

 

ところが、、、

 

その日に限って、ゴンドラは営業しておらず雲海を見ることはできませんでした、、

(雲海みたかった、、、、)

実際の雲海はこんな感じだそうです。

 

https://www.fujimipanorama.com/

 

せっかく早起きしたのに、このまま帰るのも勿体なかったので、

せめて高台で日の出でも見ようか!という話になり、

歩いて山道を登り、高台を目指しました!

 

f:id:kyoK:20201109180008j:plain

 

少し歩いたら高台に着くだろうと安易な考えで登り始めましたが、

そんなに甘くはありませんでした、、

 

予定より時間がかかった事もあり、

登っている途中で日の出を迎えることになりました。

完全に予定外でしたが、これはこれで綺麗でした!!

 

f:id:kyoK:20201109181538j:plain

 

結果的に、山道を往復で2時間程歩いてきました!

良い運動になったと言えばなりましたが、

流石に少しだけ疲れましたね、、、

 

 

最後に

2日間通して、普段では体験できない事だらけの開発合宿になりました!!

今回合宿に参加したメンバーの全員、

開発合宿に行ってよかったと思っていましたし、

もし次回行く事があれば、次は場所をどこにしようか!

次の合宿までにサービス改善案を出して、合宿内で開発しきろう!

などと行った案が出ています!

 

ストレスフリーで作業効率がめちゃめちゃ上がりメンバーとも仲が深まる!

開発合宿は良い事づくめです!

普段、都内で開発業務に勤しんでいる方、

ストレスフリーで開発をしたい方、

今まで以上にメンバーとの仲を深めたい方は

是非、開発合宿に行ってみてはいかがでしょうか?

 

エンジニア歴1年の僕がドメイン駆動設計(DDD)を参考にLaravelのプロジェクトをフルリニューアルした話

こんにちは!
はじめまして!
2020年7月からPIECE事業部でエンジニアをさせてもらっています。
野澤です。


今回、PIECEというサービスのリニューアルを担当させてもらったのでその時のことについて書きたいと思います!
まだ若輩者なので至らない点が多々あると思いますが


フルリニューアルってどんな事したんだろう〜?
Hajimariのエンジニアはどんな仕事をしてるんだろう〜?
って思った人はぜひ読んで見てください!

ドメイン駆動設計の説明も書いたのですがボリュームが多くなってしまいました…
ドメイン駆動設計について概要知りたいという方は是非読んでみてください。
クリーンアーキテクチャの説明やモデリングのやり方などは説明していません。
ご了承ください。

PIECEリファクタリングプロジェクトの概要

PIECEとはどのようなサービスなのか

PIECEとはスタートアップ向けマッチングサイト構築パッケージです。 特徴としては

  1. すでにマッチングサービスを運営するための機能が揃っているためスピード感があるビジネス展開が可能
  2. 弊社サービスのITプロパートナーズで培ったノウハウがあるためマッチングビジネスに寄り添った開発、保守が可能

です。 興味がある人はPIECEのホームページをぜひ見てみてください!


リニューアルの目的
  • 今後の保守、運用をしやすいようにソースコードリファクタリングする
  • テストについて考えられていないのでテストできるように整える
  • PIECEパッケージをもっと良くする
    の3点が主にありました。


リニューアル施策

ドメイン駆動設計適用

ドメイン駆動設計の考えを用いて層の責務をわけ、コントローラの肥大化を抑えるようにしました。
適切な責務わけをしたことにより、ビジネスロジックの再利用性も上がりました。
それとリポジトリという概念を用いてデータベース接続をORMに依存しないようにして、リポジトリのインタフェースを用意することでテストのしやすさも改善することができました。
今後はテストコードも書いていけるようにしていけたらいいなと思っています。

技術的負債を返す、既存のバグを改修

bladeを直接使わずに、フロント側のソースコードをサーバーサイドで作っていた部分があったのでそこの分離や既存で発覚していたバグの改修をしました。

DB制約の整備

本来NOT NULLのところがNULL許容されていたり、外部キー制約が設定されていなかったり整っていませんでした。
カラムに対して一つずつ精査をして正しい制約を設定しました。

型を明示的に指定する

PIECEではPHPのメソッドの引数、戻り値、プロパティの型を指定するようにしました。
型指定をするメリットは

  • 想定外の引数を受け取った瞬間にエラーになるため、不具合を検知するタイミングが早くなる
  • 関数の意図がわかりやすくなる
  • 型が保証されるので関数の引数チェックが簡単になる
    などがあります。
    安全なソースコードを書くためには方が必要だと思ったのと、僕自身HajimariでエンジニアになるまではJavaC#など型があったほうが安心感もあったので明示的に型を指定するようにしました。

コメントの強制

今まではメソッドコメントやクラスコメントが無かったのですがそれを書くようにしました。
それとコードの段落でのコメントを意識するようにしました。
理想を言うとソースコードを見ただけですぐコードを理解できればよいのですがそうはいかないときもあります。
そういうときにコメントがあれば早く理解でき、コードの改修がはかどります。

命名にこだわる

三者が見てすぐに分かるかどうかを重要視しました。
良い命名のメソッドはメソッドの中身を見なくてもどんな処理をしているかわかります。


実際にリニューアルをしてみて

今までいかにも自分がやってきた感を出しながら記事を書いていたのですが、実はそうではなく僕含め4人で対応していました。
見積もりやスケジューリング、タスクのチケットの割り振りなど経験がなく苦労しました。
ドメイン駆動設計も実務でやったことはほぼなく自分で本を読み、ネットで記事を読みPIECEに対してどのように適用したら良いのかを考え、フォルダ構成を考え色々試行錯誤しながら導入しました。
リニューアル中は上手くいかない部分がたくさんありましたがチームのみんなに協力してもらったり支えてもらったりで最終的にやり遂げることができました。
良い製品を作るためにはスケジューリング能力であったり、チームでのコミュニケーション能力だったり「技術」以外の要素も多分に必要になってくるということを実感させられました。


最後に

このPIECEプロジェクトのリニューアルに携わることになったのは入社して1週間後くらいのことでした。
まだエンジニア組織としては未熟でたくさんの課題があると思っていますが未熟な分、積極的に行動すれば改善できることがたくさんあり、挑戦できることもたくさんあります。
Hajimariの理念に共感でき、能動的に仕事をしていきたい!というエンジニアさん是非一緒に仕事をしましょう!
Hajimariではエンジニアの採用も行っているので興味がある人は見てみてください。
※下にドメイン駆動設計のちょっとした解説もあるので興味を持っていただいた方は見てみてください!

www.wantedly.com




ドメイン駆動編〜

ドメイン駆動設計を勉強するのに利用した本

www.shoeisha.co.jp ドメイン駆動設計について記事を調べたりすると抽象的な話が多い中、この本は具体的な実装例がたくさんありどのように実装していけばいいのかを知ることができる本です! 層の責務や概念はわかったけど具体的にどう書けばいいんだ!っていう人におすすめです。


gihyo.jp この本はプログラムを書くときに常に意識しておきたいことについてのことがたくさん書いてあり、かつドメインモデルの考え方や画面遷移やweb APIについてにも触れられており、
本当に現場で役に立つことが書いてありました。
ドメイン駆動設計に問わずおすすめの本です。


www.shoeisha.co.jp この本も具体的な実装例が書いてありとても参考にしました。


gihyo.jp 雑誌なのですがそこに書いてあるドメイン駆動設計がとても参考にしました。 「集約」という言葉について「そういうことだったのか!」となった本です。 ページ数は少ないですが体系的にまとめられておりその少ないページにドメイン駆動設計の情報がかなり凝縮されていて腑に落ちる部分がたくさんありとても良かったです。

ドメイン駆動設計とは

僕自身うまく言葉にできなかったのでWikipediaで調べてみたのですが
【ソフトウェアの設計手法であり、「複雑なドメインの設計は、モデルベースで行うべき」であり、また「大半のソフトウェアプロジェクトでは、システムを実装するための特定の技術ではなく、ドメインそのものとドメインのロジックに焦点を置くべき」であるとする】 と書いてありました…
抽象的すぎて分かりづらいですよね…
僕もまだ勉強の途中なので理解不足な面は多大にありますが僕自身の感覚だと「ビジネスロジックの開発の効率やビジネスロジックの再利用性を最大限上げることができる設計手法、業務(ビジネス)を中心にそえて開発を進めていくための設計手法」となります。
機能を開発するときも、改修するときもビジネスロジックに注力します。
そこでSQLAPIを意識させないようにします。
そのようにすることで業務の発展に合わせてソフトウェアを成長させることに集中することができます。


層の責務について

この記事を読んで概念がなんとなくわかるようになっていただけたら嬉しいです。 具体的な実装、コードについては別途また他の記事で書いたりしようと思います。

プレゼンテーション層

MVCのコントローラがこの層に属していると考えます。
この層は下記アプリケーション層に依存し、ユースケースを実現します。
その他http系の処理やCookie、セッション関連の処理が責務となります。
実装例.

class UserController
{
    /**
     * コンストラクタ
     *
     * @param \Application\User\UserApplicationService $service
     */
    public function __construct(UserApplicationService $service)
    {
        $this->service = $service;
    }

    /**
     * ユーザー情報参照
     *
     * @param string $id
     * @return \Illuminate\View\View
     */
    public function show(string $id): View
    {
        $user = $this->service->show($id);

        return view('user.show')->with([
            'user' => $user
        ]);
    }

}



アプリケーション層

アプリケーション層はトランザクション管理やドメイン層、リポジトリを使ったユースケースの実現が責務です。
ここにビジネスロジックは書きません。
ビジネスロジックはmodelかDomainServiceにまとめたあと、そのメソッドを呼ぶようにします。
ビジネスロジックを組み立ててユースケースを実現します。
それとドメイン駆動特有のルールではないのですがPIECEではこの層で直接ORMを利用してDB接続に関する処理を書いてはいけないという決まりにしました。
DB接続に関してはただ保存するだけ、ただ更新するだけであればリポジトリのupdateOrInsertメソッドを直接呼び出して良い、そこにビジネスロジックが入ってきた場合(データの加工をしたあとにデータ保存など)はDomainServiceを通じてDB接続をするようにします。
これらはApplicationServiceクラスに記述します。
実装例.

class UserApplicationService
{
    /**
     * ユーザー情報参照
     *
     * @param string $id
     * @param integer $companyId
     * @return \Domain\Models\User\User
     */
    public function show(string $id): User
    {
        $userRepository = new UserRepository();
        $user = $userRepository->get($id);

        return $user;
    }
}
ドメイン
ドメイン(model)

基本ビジネスロジックはここに書くことになります。
本来ドメイン駆動設計においてドメインのモデルとMVCのモデルは意味合いが違うのですが、今回PIECEでは同一のものととして考えることにしました。
ですのでドメイン=モデル=Entityといった感じで捉えるようにしています。
実装例.

class User extends Model
{
    /**
     * フルネーム取得
     *
     * @return void
     */
    public function getFullNameAttribute(): string
    {
        return $this->last_name. " " .$this->first_name;
    }
    
    // Userクラスが持つデータに対する業務ロジックを実装
}



ドメインサービス

ドメイン(model)で書くべきではないビジネスロジックを書くときにここに書きます。
modelにビジネスロジックを書くのに違和感があるとき(クラスのプロパティに対するロジックではない)や複数のドメインが関わってくるロジックのときなどに記述します。
例えば「ログイン可能かどうか」というメソッドはドメインに置くべきではないと考えます。
1ユーザーが自分がログイン可能かどうか自分自身で確認していくのは違和感を感じます。。
ですので「ログイン可能かどうか」というメソッドはドメインサービスに置くのが良いと思います。
実装例.

class UserDomainService
{
    /**
     * ユーザー重複チェック
     *
     * @param User $user
     * @return void
     */
    public function exists(User $user) : User
    {
        $userRepository = new UserRepository();
        $user = userRepository->get($user->id);

        return empty($user->id) === false;

    }
}



リポジトリ

データのやり取りの際は必ずこのリポジトリが呼ばれます。 ここのメソッドはDBの接続に関するものだったら内部でORMを利用します。
DBへの接続など意識することなく、データの入出力を実現します。
ここはDBへの接続だけではなく、例えばNoSQLを利用するようにしたりすることもできます。
ドメイン駆動設計においてリポジトリのインタフェースをドメイン層、実装クラスをインフラストラクチャ層と分類したりするのですが、フォルダの構成上の事を考えてリポジトリドメイン層の要素とみなしています。
実装例.

class UserRepository
{
    /**
     * データ取得
     *
     * @param string $primaryKey
     * @return void
     */
    public function get(string $id): User
    {
        $user = User::find($id);
        return $user ?? new User();
    }

    /**
     * データ更新or追加
     *
     * @param User $user
     * @return void
     */
    public function updateOrInsert(User $user): void
    {
        $user->save();
    }
}




依存関係について

PIECEではプレゼンテーション層がアプリケーション層に依存し、アプリケーション層がドメイン層に依存する形にしました。
もっとわかりやすく言い換えるとControlllerがApplicationServiceのメソッドを呼びます。


フォルダ構成について

ドメイン

package
│   ├── Domain
│   │   └── Models
│   │       ├── Company
│   │       ├── User

ドメイン層に関しては上記のようにModelsの下にmodelの名前のフォルダを作り、その中にドメイン(model)、ドメインサービス、リポジトリインタフェース、リポジトリを格納しています。
フォルダ構成についてはいろいろな考え方があると思いますがPIECEでは各要素を一つのフォルダに加えることで、ソースコードを見つけやすくなると考えてそのような構成にしました。
例えばUserに関する機能を作るときはUserの下のドメインドメインサービスを見れば再開発しなくても済むかもしれないと予想することが用意になります。

アプリケーション層

packages
│   ├── Application
│   │       ├── Company
│   │       ├── User

アプリケーション層に関しては上記のように構成しています。
CompanyやUserというフォルダにはApplicationServiceとDTOのファイルが格納されています。
DTOとは【Data Transfer Object】のことで、関連するデータを一つにまとめた、データを運ぶだけのオブジェクトです。
PIECEではApplicationServiceのメソッドではドメイン(model)かDTOインスタンスをControllerに返すようにしました。

ドメイン駆動設計の考え方を用いてリファクタリングした感想

ドメイン駆動設計は短期間でのリファクタリングや新規開発などスピード感を求められる場面ではあまり向いていないと思いました。
実際にドメイン駆動設計の考え方を取り入れたときにネックだったなと思ったのは

  • 責務わけをしたことによりファイルの数が多くなり対応に時間がかかった。
  • ドメイン駆動設計にどこまで乗っ取るのか決めるのに時間がかかった。(「entitiy」、「valueobject」、「仕様」、「集約」、「ファクトリー」などドメイン駆動設計で重要な概念があるがスケジュールのことや今後のことを考えて使用しなかった)
  • チームでドメイン駆動の概念の理解やドメイン駆動設計に基づくルールに則ってちゃんとコードに起こせるようになるまでのコストが高いと感じた。
  • 集約の概念が難しい。「リポジトリでのやり取りの基本単位が集約」、「不変条件を維持する単位」など頭でわかっていてもモデリングを時間をかけてやらないと上手くいかない部分が多々出てきた。

という4点です。
プロジェクトの規模にもよりますが、リファクタリングをするために時間をたくさんかけることができる状態であり、
入念にドメインエキスパートの人に話を聞いてモデリングして実装に起こしていくというふうにすすめていかないとレベルの高いドメイン駆動設計は難しいと思いました。
ただし軽量ドメイン駆動設計でも多分にコードのみやすさであったりビジネスロジックの再利用性は上がったので良かったと思っています。