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

JavaDo勉強会での学び実践

Avatar for sai-lens sai-lens
October 24, 2025

 JavaDo勉強会での学び実践

Avatar for sai-lens

sai-lens

October 24, 2025
Tweet

More Decks by sai-lens

Other Decks in Technology

Transcript

  1. ⾃⼰紹介 • さい/sai-lens • 現在は病院で臨床検査技師として勤務 • プログラミング学習3ヶ⽉⽬ • 現在学習中の技術 ◦

    フロントエンド:JavaScript, React ◦ バックエンド:Ruby, Ruby on Rails 1 @GTposiwill @sye-lens
  2. 本⽇のLTのきっかけ • 勉強会に参加 ◦ JavaDo「増田 亨さんと設計の実践的な考え方 を学ぼう!」 ◦ ⽇時:10/4(⼟) ◦

    場所:クオリサイトテクノロジーズ株式会社 (⽇本⽣命ビル) • 勉強会での学びをアウトプットして⾝につけたい 2
  3. どう実現するか? → 「関⼼の分離」 モデル駆動設計(MDD)の考え⽅:役割ごとにコードを分ける UI (表⽰‧操作) ユーザーが⾒る画⾯や、 操作を受け付ける部分 インフラ (DBなど)

    データを保存したり、 外部と通信したりする部分 Domain (ロジック) 「アプリの核となるルール」 を決め、考える部分 5
  4. ディレクトリ構成 domains/attendance_decision.rb はcontrollers, models, viewから独⽴ / ├─ app │ └─

    assets │ ├─ controllers │ ├─ domains │ │ └─ attendance_decision.rb │ ├─ models │ └─ views ├─ db 8
  5. domains/attendance_decision.rb class AttendanceDecision AttendanceDecisionResult = Struct.new(:decision_status, :most_suitable_date, keyword_init: true) def

    initialize(members:, required_members:, proposed_dates:, responses:) @members = members @required_members = required_members @proposed_dates = proposed_dates @responses = responses end def decide if (d = decided_date) return AttendanceDecisionResult.new(decision_status: :decided,most_suitable_date: d) end AttendanceDecisionResult.new(decision_status: :reschedule_required, most_suitable_date: best_date) end private def decided_date @proposed_dates.find { |d| all_yes?(d) && required_all_yes?(d) } end . 9
  6. 良い設計:attendance_decision.rbを修正するだけ class AttendanceDecision AttendanceDecisionResult = Struct.new(:decision_status,:most_suitable_date) . . def decide

    if (d = decided_date) return AttendanceDecisionResult.new( decision_status: :decided, most_suitable_date: d, . end private def decided_date @proposed_dates.find { |d| all_yes?(d) && required_all_yes?(d) } end . . 14 if (d = decided_by_required_all_yes_tiebreak) return AttendanceDecisionResult.new( decision_status: :decided, most_suitable_date: d, ) end # 必須参加者が全員◯の候補のみを対象に、 # 「total_yes」が最大のスコアで同点の候補が複 数ある場合は最も早い日を返す # そうでなければ nil def decided_by_required_all_yes_tiebreak candidates = @proposed_dates.select { |d| required_all_yes?(d) } return nil if candidates.empty?
  7. 悪い設計:controllersでI/Oとロジックが混在 15 class AttendancesController < ApplicationController def update attendance =

    Attendance.find(params[:id]) meeting = attendance.participant.meeting if attendance.update(attendance_params) decision, scores = meeting.decide_and_score respond_to do |format| format.turbo_stream do render turbo_stream: [ ] end else . . end private def attendance_params params.require(:attendance).permit(:status) end end # ①「全員◯ & 必須◯」なら即 decided decided_pd = date_rows.find do |pd_id, _d| all_yes_for.call(pd_id) && required_all_yes_for.call(pd_id) end if decided_pd decided_date = decided_pd[1] decision = OpenStruct.new(decision_status: :decided, most_suitable_date: decided_date) else # ② 機能拡張:必須は全員◯の候補のみ抽出 required_yes_candidates = date_rows.select { |pd_id, _d| required_all_yes_for.call(pd_id) } if required_yes_candidates.any? # total_yes の最大値を算出 with_total = required_yes_candidates.map { |pd_id, d| [pd_id, d, total_yes_count_for.call(pd_id)] } max_total = with_total.map { |(_id, _d, tot)| tot }.max . . viewへの出力 DBから入力データを取得
  8. まとめ - 「良い設計」は「悪い設計」よりも変更しやすい - 「変更に強い設計」を実践、メインロジックをその他のI/Oから分離した - その結果 - メインロジックの機能拡張、変更が「楽」 -

    メインロジックに⼲渉しないのでその他の変更が「安全」 - どこに何が書いてあるのかが理解しやすく、誰にでも意図が伝わる コードになる 16