Coder Social home page Coder Social logo

business-logic-patterns's People

Contributors

abetd avatar baki504 avatar masuda220 avatar wing3298 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

business-logic-patterns's Issues

給与計算モデルの実装とサービスのテストの追加

  • domain.model.payroll.daily 以下の骨組みクラスを実装する
  • テストは、 application.service.payroll.DailyPayroll クラスに対して作成する

参考

https://keisan.casio.jp/exec/system/1245199831

この画面を参考に、

  • 入力:開始時刻、終了時刻、休憩時間(分)
  • 出力:日給の金額

のみの簡易版のサービスを実装する

端数計算

  • 1分単位で計算する
  • 円単位で切り捨てる

Quantity型の足し算と引き算で大きな単位への変換は「例外」にする

Quantity型のオブジェクトの単位がBoxUnitの時、引数が、PieceUnitの足し算・引き算は、例外にしましょう。
ただしく変換できない可能性があるので、安全のため自動変換はしないで、例外をthrowする。

いったんは、制限付きの単純な仕様にしておく。
実際には、PieceUnitからBoxUnitへの変換も必要なので、その実現方法は、別途検討する。
(変換用の型を作ったほうが良いかも)

という方向で、以下の修正をする。

  • Unit#isEqualTO() の実装で、 class() の比較に加えて piece() の比較もする (BoxUnitのオブジェクトは、異なる pieceを持つ可能性があるため)
  • Quantity#opedand() の実装で、piece->boxの変換は、例外をthrowする
  • Quantity#opedand() の実装で、else 句を削除する 。早期リターン(if文から直接return)なので、else句は不要)
  • Quantity#opedand() の名前を、例えば、alignUnit() とか、意図を具体的に表現する名前を工夫する

金額(円)計算をlongで扱う、Amount型の試作

  • 加算と減算は、Amountに閉じる
  • 乗算は、いったんは、int を渡す
  • 除算は、いったんは、int を渡して、Amountの配列(商と余り)を返す
  • どの場合も、longのオーバーフローは、例外をthrowする

UnitPriceで、異なるUnitのQuantityの乗算はいったん例外とする

Unitの自動変換は、魅力的な機能ですが、ビジネスルールが暗黙化するイヤな臭いも感じています。

単位の変化のあるべき仕様について、別isseeで議論したいと思います。

いったんは、より型チェックを厳しくする方向で、次の変更を行う

  • UnitPrice#multiply(Quantity)では、UnitPriceのUnitと、Quantityのユニットが不一致の場合は例外とする
  • Unitが一致しているかの判定メソッドを追加する。例えば: UnitPrice#hasSameUnit(Quantity)

日付範囲 DateRange型の試作

  • 二つのLocalDate 開始日を終了日を持つ
  • いったんは、開始日と終了日の両方とも必須とする
  • 開始日<=終了日であること
  • ある日付を与えたら、期間内、期間前、期間後の判定結果を enum 型で返す
  • 開始日と終了日は、当日を含む

AmountRange型のメソッドコメントの削除

メソッド名、引数の型名/変数名、返す型名での説明力を改善のために、メソッドコメントは、書かないという方針でお願いします。
(設計ガイドラインを追加しました)

https://github.com/masuda220/business-logic-patterns/wiki/%E8%A8%AD%E8%A8%88%E3%82%AC%E3%82%A4%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AEjavadoc%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88

Amountの比較演算の追加

isEqualTo(Amount) 以外に、以下の比較演算を追加する

isGreaterThan
isGreaterOrEqualTo
isLessThan
isLessOrEqualTo

pieceとboxの変換の仕様

単位の変換の仕様についての検討。

基本コンセプトの選択肢は三つ

  • A. できるだけ厳密に単位の一致をチェックする (暗黙の変換はしない。すべて明示的に変換する)
  • B. できるだけ自動で変換する (自動変換できない特殊な場合だけ、例外とする)
  • C. 原則、単位の一致チェックをするが「自然な場合」(例えば、大きな単位から小さな単位への変換)」は、暗黙の単位変換を実装する

実際に使うシーンを想定しつつ、考え方を検討したい。

  • それぞれの方向のメリット、デメリット
  • それぞれの方向での具体的な仕様(メソッド案など)
  • それぞれの方向の仕様の評価

<参考>
Javaのプリミティブな数値型は、short->int->long->double というより大きな数値を扱える型への変換は、暗黙の変換が行う。
ただし、long->doubleの変換では、厳密には、値が変わってしまう(もとに戻せない)

暗黙の型変換は、便利ではあるが、「暗黙」のルールであることがデメリット。

ドメインロジックを表現する型の設計では、暗黙の変換は、基本的にさけるべきではないか?

状態は、期待するイベントの知識を持つ

#61 で実装したゲートの状態遷移を扱うサンプルで、State が、期待するイベントの知識を持つようにする。

以下の二つのメソッドを追加する

  • State#isExpected( Event event ) : boolean
  • State#expectedEvents() : Event[]

補足:

  • isExpected は、イベントを受け取った「後」で、イベントの妥当性を検証するため
  • expectedEvents は、イベントを受け取る「前」に、クライアントが有効なイベントを知る手段を提供するため

実装:

実装は、Transitions に上記の二つのメソッドを持たせて、責任を委譲する。

ライブラリの実装仕様の説明の記述方法

コードだけで説明しきれない個所がでてきている。
(桁数の扱いの考え方など)

現時点の設計ガイドラインは、コメントを書かない。
ある程度は、書いたほうがよさそうだが、できるだけ書かないほうが望ましい。

アイデア

  • クラスのJavaDocをある程度は書く
  • package-info.java に、そのパッケージの基本的な考え方を書く
  • 詳細な仕様(契約)は、テストコードで表現する

Amount型に複数のAmountの足し算を追加

Amount に以下の足し算を追加

Amount addAll(Amount... amounts)
Amount addAll(Collection<Amount> amounts)

static Amount from(Amount... amounts) // return new Amount(0).addAll(amounts)
static Amount from(Collection<Amount> amoutns) // 同上

23:59 を超える時刻(時分)を扱う型の試作

LocalTime型は、23:59 までしか扱えない
String で、26:00 を渡した場合、24H + 2H であることを扱える型を作ってみる

用途としては、開始時刻と終了時刻の時間間隔の計算

状態遷移 eventから状態依存のコードを削除

#61 で次の状態は、State が持つ ( Transitionsを持つ)設計に変更した。

その結果、 Event の next() メソッドと、インスタンス変数 State nextState が不要になった。

不要になったメソッドとインスタンス変数を削除する。

Eventは、単なる定数オブジェクトになるが、いったんはそれでよしとする。

割り勘のミニマムな試作

https://keisan.casio.jp/exec/system/1244792650
を参考に

以下のサービスを試作する

  • 支払い総額と人数を指定する
  • 100円単位で一人当たりの金額を返す
  • 不足額を返す

実装の指針

  • application.service.warikan パッケージ以下にサービスクラスを追加(テスト対象)
  • サービスのメソッドは、パラメータが(総額、人数)で、返す型は Amount[] (一人当たりと不足額)
  • domain.model.warikan パッケージ以下にモデルクラス群を追加
  • domain.type.money など 必要であれば、基本型の追加や修正を行う

固定小数点数を扱う DecimalAmount型 の試作

これは、ひとつのissueでやるには、たぶん大きすぎるテーマです。
考えることがたくさんありそう。

もしやっていただける場合は、いくつかのissueに分解して、進めていただければと思います。

概要:小数点以下2桁の金額(円の場合は、銭単位まで)の四則演算

試作仕様は、

  • 小数点以下2桁固定
  • 乗算、除算の引数は、整数(int)
  • 除算の場合、小数点三桁を四捨五入する
  • 小数点以下を四捨五入して、Amount型(整数の金額)を返すメソッドを用意する
  • Amount型から、long値を取り出すメソッドが必要かもしれない。
  • それ以外は、Amount型は、この型の実装のために、変更はしないことが望ましい

実装のアイデアとしては、

  • Amount型を non-scale value として持つ、あるいは、long値をもち、適宜、Amount型を使用する
  • int で、小数点以下の scale値を持つ( 試作版では、2桁固定)
  • つまり、最大金額は、Amount型の最大金額 (LONG_MAX)の 100分の1である
  • コンストラクタは、文字列表現( "12.34")のみ、取り込む
  • toString() は、コンストラクタに渡せる文字列表現を出力する

留意点としては、

  • BigDecimalのAPI ドキュメントとソースコードを参考に試作する
  • 将来的には、小数点以下の桁数は、1~4の4種類を持つことを想定
  • 将来的には、四捨五入以外に、切捨て、切り上げ、「銀行家の丸め」も対応したい
  • まずは、今回の試作仕様で、割り切りバージョンで動かしてから、段階的に進化させていく

Percentage型, Premillage型の試作

基本の仕様 (Percentage型)

  • 内部に百分率を持つ(コンストラクタで百分率を指定する)
  • 不変オブジェクト
  • 有効な値の範囲は0%~100%
  • 有効な操作は、Amount(long範囲の正の整数)に対する掛け算
  • 掛け算の種類は、Amountに収まらない端数の切り捨て、切り上げ、四捨五入

追加の仕様

  • 内掛け(inner percentage:内税方式の税額の計算方法)ロジックの実装

留意事項

いつもの通り、用途を限定したミニマムな仕様を重視する。
今回、想定している主なユースケースは消費税の計算。

百分率という概念を実直に表現すること。

固定小数点数の桁数制御の厳密化

DecimalAmountの小数点以下の桁数の扱いは、BigDecimalと同様に、桁数が多くなる方向(情報を失わない方向)にすることにした。

用途によっては、桁数が自動的に(暗黙に)変わることは好ましくない可能性がある。
桁数チェックを厳密する方法として、以下の2つを検討する

  • addExact() メソッドを追加し、桁数の不一致は例外とする
  • ExactDecimalAmount クラスを別に作る(すべてのメソッドで、桁数不一致を厳密に行う)

ピースと箱の換算ができるQuantity型の試作

  • 数量の加算、減算、乗算ができる
  • 数量は、整数値と単位(Unit)を持つ
  • 加算、減算、乗算は、元の値の単位に合わせる(ピースに箱を足すと、ピースになる)
  • ピース単位を箱単位に、箱単位をピース単位に変換できる
  • いったんは、単位は、ピースと箱の二週類で、列挙型で固定で宣言する

enum Unit {
ピース,

}

stateパッケージを追加して、状態遷移演算の試作

状態タイプ

  • opened
  • closed
  • locked

ミニマム実装

  • occurs(イベントタイプ)メソッドで、遷移後の状態タイプを返す
  • 不適切なイベントは、例外を返す(Java の標準の例外で、なにか適切なもの)

イベントタイプ(遷移タイプ)

  • open
  • close
  • lock

ユースケース 時給計算の骨組みを追加

具体的なアプリケーションとして、時給計算サービスを想定して実装してみる。
そのための骨格のクラスを用意する。

サービスクラスの追加

application.service.payroll.DailyPayroll
メソッド DailyPayroll#for(TimeRecord record, HuorlyWage wage) : Amount

参考画面

https://keisan.casio.jp/exec/system/1245199831

この画面の簡易版
入力:勤務情報(勤務開始、終了、休憩時間)と時給
出力:日給
を実現するためのサービス

domain.model.payroll.daily

上記のサービスを実現するためのモデルクラス群(骨組みのみ)

  • TimeRecord
  • HourlyWage
  • DailyPay

時間のrounding

#66 で実装された内容の追加の検討

アイデアレベルですが、#66の実装を受けて、考えたことを書き出してみます。

MinuteUnit

このクラスは、wage モデルよりも、domain/type/hour のほうがよいかもしれない。
Minuteの割り算として、実装する

  • Minute#divide(divisor)
  • Minute#divide(divisor, roudingType)

type.RoundingType (enum) で RoundOff, RoundDown, RoundUp を宣言する

端数計算

double で計算して、(int)でキャストしているが、言語仕様を暗黙に使っているところが、ちょっと違和感がある。

BigDecimal を使うと、冗長になるが、その分、説明的になるはず。

Math.floorDiv()

これは、負の数を、無限大の方向に切り捨てるための関数だったはず。
今回の文脈(正の数が前提)の意図としては、違和感がある。(通常の切り捨てでよい)

計算単位(今後の拡張)

Minute クラスを、5分、10分、15分、20分、30分単位に Rounding できるメソッドがほしい

例えば、enum MinuteUnit で、上記の分刻みを宣言して、
Minute#byUnit(MiniteUnit, RoundingType) : Minute
みたいな感じ

@baki504 @abetd 意見をいただけますか?

date.ContainType型と DateRange.contains() メソッドの名前、あるいは設計

ある日付を与えたら、期間内、期間前、期間後の判定結果を enum 型で返す

この仕様を元に試作した ContainType型、contains() メソッドの名前がしっくりこない。

よい名前が見つからないということは、たぶん、仕様か設計に改善の余地があるはず。
すぐに良い名前/設計が見つからないかもしれないが、いちおう備忘録として、issueを追加しておく。

現時点での所感:

三つのいずれかを戻す、という仕様が、不自然なのかもしれない。
例えば、次のように分解するとかも選択肢として検討したほうがよいかも。

  • 期間内の判定 ( 期間内・期間外を返す)
  • 期間前かの判定 (期間前・期間前ではない)
  • 期間後かの判定 (期間後・期間後ではない)

具体的に使うシーンを考えたほうがよさそう。
机上で考えていてもしょうがないので、DateRange型を使うAggregate型(ロジックを集めて複合型)を検討してみる。

例えば、キャンペーン期間に対して、割引の適用可否とか。

Days型をdateパッケージに追加する

Days は、日数を表現する

Weeks は、日数を週単位で表現する( 内部に Daysを持って、週数または週数+日数を表現する)
( #37 に独立させた)

ミニマムな実装案
(別コメントを追加)

HourTime 型, Hour型, Minute型、HourAndMinute型の試作

hour パッケージで、時刻と時間を「時分」単位で表現する型を設計する。

概要

  • HourTime 型 時刻を時分単位で表す(秒を扱わない)
  • Hour型 「時間(数)」: 時刻と時刻の間隔。
  • Minute型 「分(数)」: 時刻の時刻の間隔(60分以上も、扱う)
  • HourAndMinute 型 : x時間y分 内部に Hour と Minute を持つ

想定するユースケース:
https://keisan.casio.jp/exec/system/1245199831

この給与計算の機能の中で、「勤務時間の計算」をするためのクラス群を試作する

DecimalAmount型の機能拡張(桁数の異なる計算)

@abetd さんからの #9 で提起された検討課題

現在は小数点以下2桁で実装していますが、
小数点以下の桁数を1〜4にの4種類にする実装の際に、
異なる桁数の加算減算は可能とするか、
可能な場合は計算結果の桁数はどうするかなどが気になりました。

仕様を検討する際には、具体例で考えたい。

date パッケージに Weeks 型を追加

日数を週単位で表現する( 内部に Daysを持って、週数または週数+日数を表現する)

前提: #8 Days型の実装が終わっていること

ミニマムな実装案

  • コンストラクタ

    • Weeks(Days dasy)
  • メソッド

    • add(Days) / add(Weeks)
    • subtract(Days) / subtract(Weeks)
    • isEqualTo() / isGreaterThan(), isLessThan()
    • show() // "1週間" "1週間と1日"
    • toString() // "1weeks1days”

金額範囲 AmountRange型の試作

金額範囲(xx円からyy円)を表現する型

  • 金額を与えたときに、その金額が範囲内かどうかを判定する
  • いったんは、最大・最小必須 (最大のみ、最小のみという指定は、将来検討)
  • 最小「以上」で、最大「未満」で判定する
  • money パッケージに追加する

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.