DriveSFAではカスタム項目に対応しています。カスタム項目というのは、システムが規定している項目以外にクライアントさんが自社用に追加できる項目のことです。 このカスタム項目を実現するのが意外に難しいです。 RDBを使う以上json型を使うのが手っ取り早いのですが、一覧のための部分一致検索がしづらいです。 ただ、SFAということでテーブル間の関係性が重要なので、RDB自体は使いたいです。 そこで、カスタム項目だけMongoDBに格納して対応しています。
カスタム項目でもう1つ難しいのは、設定変更時の互換性維持です。 項目の追加・削除・編集に対応する必要があります。 RDB上にカスタム項目の設定は構造化して持たせ、個別の項目の具体的なデータについては紐づく設定を元に最新の設定がわかるため、 設定の変更を個別のデータに反映しなくてもAPIに必要なデータの取得時に最新の設定を元に合わせることで対応できます。 ただ、項目の型の変更を許すと値の型がバラバラになり処理しづらいため制限しています。
具体的な項目を表すクラスの定義は以下となります。
class PropertyDto { lateinit var logicalName: String lateinit var physicalName: String @field:Schema(required = true) var inputTypeKindId: Long = 0L @field:Schema(required = true) var value: ValueDto? = null @field:Schema(required = true) var values: List<OptionDto> = emptyList() @field:Schema(required = true) var officialFlag: Boolean = false @field:Schema(required = true) var requiredFlag: Boolean = false @field:Schema(required = true) var options: List<OptionSettingResponseDto> = emptyList() @field:Schema(required = true) var orderNumber: Long = 0 }
inputTypeKindIdがプロパティの種類を表し、requiredFlagで必須かを、orderNumberで順序を表しています。 また、officialFlagという、システムが規定している項目かを表すフラグもあります。設定とリクエスト・レスポンスにおいては、 システムの規定項目もカスタム項目と同じように扱っているからです。ただ規定項目はより編集・削除に対する制約を厳しくしています。 項目の値については単一の値の場合と複数選択肢の場合があるので、valueとvaluesという2つを用意しています。 単一の値を表すクラスは以下となり、型ごとに変数を定義しています。
class ValueDto { @field:Schema(required = true) var id: Long? = null var stringValue: String? = null var longValue: Long? = null var doubleValue: Double? = null var localDateValue: LocalDate? = null var localDateTimeValue: LocalDateTime? = null var booleanValue: Boolean? = null }
valuesの要素であるOptionDtoは以下のような定義となり、選択肢のIDを保持することで、 選択肢の設定の変更に対応しています。
class OptionDto { var id: Long = 0 @field:Schema(required = true) lateinit var name: String }
これまで説明させて頂いた方法をとることで、Entity Attribute Value(EVA)のような管理しづらくパフォーマンス的にも難しいデータ構造を避けることができます。