メンバー数の上限の制約は Member の責務でも Members の責務でもなく、一つ上の集約の責務だと思う
「メンバー数の上限って Member の責務ですか?」
「いや Members の責務では?」
いや、メンバー数の上限は Member の責務でも Members の責務でもなく、所属する一つ上の集約モデルの責務でしょう。
例えばこういうやつです。
- Team
- Group
- Organization
上限を決めるのはそこです。
なぜ Member ではないのか
例えば「このチームは最大 5 人まで」みたいな制約があるとします。
このとき Member 一人だけを見ても、上限に達しているかは判断できません。 なぜなら、その判定には「自分以外の所属メンバー」と「所属先の制約」が必要だからです。
つまりこれは、個々の Member が単体で背負う責務ではないと思います。
Member はあくまで「所属している一人」を表す存在であって、「あと何人入れるか」を決める主語ではないです。
なぜ Members でもないのか
ここで Members のようなコレクション側に寄せたくなることもあります。
たしかに人数の話ではあるので、一見それっぽいです。 でも個人的にはここも違うと思っています。
なぜなら、上限という制約はメンバー集合そのものに生えているわけではなく、その集合を持つ親側の制約だからです。
例えば、
- Team は最大 5 人
- Organization は契約プラン次第で最大 20 人
みたいなことは普通にあります。
このとき上限は Members 自体の制約ではなく、Team や Organization 側の制約です。 なので Members に責務を寄せると、少し主語がズレるかなと思います。
上限が生えるのは所属先
結局、メンバー数の上限は「どこに所属するか」で決まります。
なので責務を持つのは Member でも Members でもなく、その一つ上の集約です。
- Team が何人まで受け入れられるか
- Group が何人まで所属可能か
- Organization が何人まで保持できるか
この制約を持つのは親側です。例えば
type Member = { /* ... */ } type Team = { members: Member[] } const TEAM_MEMBER_LIMIT = 5 const canAddMember = (team: Team): boolean => { return team.members.length < TEAM_MEMBER_LIMIT }
大事なのは判定の主語が Team になっていることです。
Team の上限が 5 人なのに 6 人目を入れた場合、壊れるのは Member ではなく Team の整合性です。
つまり守るべき不変条件はこういうことです。
- Team は 5 人を超えてメンバーを持ってはいけない
- Group は定められた上限を超えて所属を持ってはいけない
- Organization は契約上限を超えてメンバーを保持してはいけない
これなら責務の置き場所はかなり素直かなと思います。
なので責務を考える出発点としては、 メンバー上限は親集約の責務 と考えるのが自然かなと思っています。
最後に
実際に Member や Members 側へ責務を寄せている実装を結構見ます。
たしかに人数の話なので、ぱっと見では Members に置きたくなる気持ちもわかります。
でも違います。 所属する一つ上の集約モデルの Team、Group、Organization に上限の制約を仕込むのです!!以上です!!
自分だけのClaude Code Skillsは*.localで逃がしている
最近 Claude Code Skills をちょこちょこ増やしています。 便利なのですが、使っているうちに「チームで共有したい Skills」と「完全に自分専用の Skills」は分けて扱いたくなってきました。
例えば、自分専用にしたいのはこんなものです。
- 自分のマシンのディレクトリ構成に依存している
- 自分だけの作業フローや運用ルールを前提にしている
- そもそも共有する価値は薄いが、自分には便利なもの
こういう Skills を通常の ./.claude/skills/ 配下にそのまま置くと、git 管理したくないものまで同じ場所に並んでしまいます。
見た目には些細ですが、運用上はこの混ざり方が少し気持ち悪いなと思いました。
そこで、僕はローカル専用の Skills だけディレクトリ名に .local を付けて逃がしています。
やり方
例えばこんな感じです。
./.claude/skills/<my-private-skill-name>.local/SKILL.md
そして .gitignore には下記を入れています。
.claude/skills/*.local/
これだけです。
今のところ問題なく使えている
この構成でも Claude Code 側は普通に読んでくれるようで、今のところ動作面で困ったことはありませんでした。
つまり、運用としてはこんな感じに分けられます。
- チーム共有したい Skills は通常のディレクトリ名で管理する
- 自分専用の Skills は
*.localにして git の管理対象から外す
かなり素直です。 余計な工夫をせずに、共有用とローカル用の責務を分けられるのがよいです。
感想
こういう仕組みは、最初は「全部リポジトリで管理するもの」と考えがちだと思います。 ただ実際には、Skills ってかなり環境依存なものも作りたくなります。
特に Claude Code を日常的に触っていると、
- このディレクトリを前提に動いてほしい
- このファイル群を見に行ってほしい
- この手順は自分の環境だけで成立すればいい
みたいな、自分特化の小さな Skills が自然に増えていきます。
そのたびに「これは共有すべきか?」を考えるのは少しだるいので、最初から *.local という逃がし先を作っておくと気が楽かなと思いました。
しばらくはこの運用でハッピー Claude Code Skills ライフをやっていこうと思います。
以上です。
RDP接続ではWindows端末に繋いだカードリーダーで署名できずe-Tax/eLTaxの中間申告でハマった
ひとりで経理頑張ってるマンです。
半年前に初めての決算を終え、法人初年度を乗り越えました。 そして2期目に突入し、一通り経験した経理業務には余裕を持って取り組んでいました。だが、そこに「予定納税」というお初の子がやってきました。初年度には居なかったじゃん。*1
焦ってやり方を調べたのですが、普段使っている経理系のクラウドサービスでは対応していないようで、e-Tax, eLTax を使って納税することにしました。やり方もググれば出てきて、本当インターネット最高!
読みながら進めていくうちに Windows PC でしか使えないネイティブソフトが必要なことに気付きます。仕方なく動画の wmv 変換用に買った miniPC を立ち上げて RDP で作業を進めることにしました。
操作に手こずりながらも送信データの署名部分 *2 まで持ってこれたのですが、ここから先がうまくいかず。。。
カードリーダーとマイナンバーカードで署名しようとするが、どうやってもうまくいかず。ドライバの問題かなと思って調べていましたが、どうやら RDP だとカードリーダー読み込みができないようでした。
mini PC だからドライバ周りが特殊なのかも〜、DVD レコーダーは動くからカードリーダー自体の故障なのでは〜、と勘所が悪く変に時間をかけてしまいました。。。
その後 mini PC に直接ログインするとあっさり署名できました。。。。涙
来年同じところでハマらないように残しておきます。*3
Railsで参考にした公式っぽい命名のソース
Rails・Ruby
値オブジェクト名
メソッド名
バリデーションメソッド名
レコード全体のステートに関連するエラーの場合 record.errors[:base]
- must_be_xxx
- ref: must_be_friends : 友達である必要があります
- imo:
- must_be_active : 有効である必要があります。
- must_be_authenticated : 認証しなければならない
特定の属性に関するエラーの場合 record.errors[:some_field]
- xxx_cannot_be_xxx
- imo:
コールバックメソッド名
- set_xxx
- ref : set_status
- remove_xxxx
- ref : remove_whitespaces
- 認証チェック系
- ref:
- authenticate_user! : Devise gem
- require_login : Sorcery gem
- ref:
GitHub CLIを使ってGitHub ActionsのWorkflowを検証ブランチで試す
repository 内に検証ブランチを作成し、検証に必要なコードや下記のような workflow 定義 yml を配置する
- workflow 定義 yml で actions/checkout を利用して、検証ブランチを指定
- 任意のタイミングで実行できるようにトリガーに workflow_dispatch も設定
# 定義例 ... on: workflow_dispatch: ... jobs: xxxx: ... steps: - uses: actions/checkout@v3 with: ref: <検証ブランチ> - name: my job 1 run: | ....
実行する際は GitHub CLI を利用して、下記のようなコマンドで workflow を実行する
GitHub CLI | Take GitHub to the command line
$ gh workflow run [<workflow-id> | <workflow 名>] --ref 検証ブランチ
これで Workflow を検証ブランチで試せます。
この方法で、手元でコードや yml を修正し、検証ブランチへ push。手元でそのまま gh workflow run を実行するような流れで検証しています。
おわり
GitHub Actions に限らずですが、CI・CD 周りの検証には骨が折れます。。。
今回、workflow 内で checkout の設定をしておらず、GitHub CLI では 検証ブランチ に向けて試していて、yml は検証用なのにそれ以外が本流のままだったとうっかりミスで数時間を潰してしまったので、備忘録的に書いてみました。
ActionController::Renderers.add で render_xxx メソッドを撃退
Railsで開発している際、コントローラー内で render_xxx のような独自メソッドをよく見かけます。特に API を構築する際に、render_api や render_json などのレスポンス構成を共通化するために、render json: の処理をラップしたコードをよく目にします。
以下のようなコードです。実際には内部で別のプライベートメソッドを呼び出すなど、複雑な実装が含まれていることもあります。
# api を返すときはこのメソッドを使う ← よく見るコメント def render_api(object, status: 200) render json: { data: object, status: status } end # 利用部分 render_api(record, status: 200) # json レスポンス # {"data":{"id":1,"created_at":xxx,"updated_at":xxx,"name":"john"},"status":200}
しかしこのアプローチには何か違和感があります。特には複数のプライベートも重ねて実行されることもあり、無駄に application_controller のようなベースコントローラにつらつらと書かれていることも多く、見てて気持ちいい実装ではありませんでした。
常に同じ形式で使用するのであれば、render api: xxx のようなシンタックスがよりスマートであると感じるのではと。
ActionController::Renderers.add による改善
この違和感を解消するための方法として、 ActionController::Renderers.add で renderer を追加するやり方があります。
これを導入することで、render hoge: xxx のような記述でレスポンスを返すことができるようになります。
# config/initializers/add_controller_renderer.rb ActionController::Renderers.add :api do |object, options| json_response = { data: object, status: options[:status] || 200 } render json: json_response, status: json_response[:status] end
上記のコードを追加するだけで、以下のようにレスポンスを生成できます。
# コントローラ内で下記のように利用可能 render api: record, status: 200 # json レスポンス # {"data":{"id":1,"created_at":xxx,"updated_at":xxx,"name":"john"},"status":200}
個人的にはこっちの方がより Rails チックでスマートな記述に感じます。 このアプローチを取ることで、独自のレンダリングメソッドをコントラーラ内部につらつらと追加する必要もなくなります。
api のレスポンスの他にも react 描画用の html などのテンプレ形式で返す際にも便利なのでおすすめです。