マイクロサービス環境で新しいサービスを開発しています。これはRESTサービスです。簡単にするために、パスが/ historyBooksであるとしましょう。
そして、このパスのPOSTメソッドは、新しい履歴書を作成します。
歴史書が歴史の1つ以上の時代を扱っているとしましょう。
簡潔にするために、私たちには人類の歴史の次の時代しかないと仮定しましょう:
私のコードでは、それらをenum
で表します。
メソッドの本体(ペイロード)はJSON形式で、フィールド名eras
を含める必要があります。このフィールドは、この本がカバーするera
値のリストです。
ボディは次のようになります。
{
"name": "From the cave to Einstein - a brief history review",
"author": "Foo Bar",
"eras": ["Ancient", "Post Classical", "Modern"]
}
この特定のサービスのビジネスロジックは次のとおりです。
入力に年号が指定されていない場合、この本はall年号をカバーすると見なされます。
APIレビューでは、次の提案が行われました。
年号列挙に別の値ALL
を含めて、すべての年号がカバーされていることを明示的に示します。
私にはいくつかの長所と短所があると思います。
長所:
明示的な入力
短所:
リストの2つの項目が提供されている場合は、ALL
およびAncient
と言います。アプリケーションから何が取得されますか? ALL
は他の値をオーバーライドする必要があると思いますが、これは新しいビジネスロジックです。
クエリを実行すると、特定の時代をカバーする本について、すべての時代をカバーする本をどのように表すことができますか? ALL
が出力にも使用されている場合(同じロジックを使用)、ALL
を["Ancient", "Post Classical", "Modern"]
として解釈するのは消費者の責任です。
新しいALL
を使用すると、まったく使用しない場合よりも混乱が生じると思います。
どう思いますか?このALL
値を追加するか、それなしでAPIを保持しますか?
使用可能な年代が呼び出し元のアプリケーションで使用可能かどうかによって異なります。おそらくそれらは、ユーザーが興味のあるものを選択できるようにするためのものです。その場合、「すべて」のオプションを提供することはフロントエンドの問題です。もし私だったら、「すべて」のオプションではなく、すべての時代のリストを送信するでしょう。そうでない場合は、「ALL」オプションが必要であり、フロントエンドが理解できない時代から物事を取り戻すというリスクを負うことになります。
ご指摘のとおり、他のオプションを使用したALLは、競合するリクエストを取得できることを意味します。もう1つの考慮事項は、 "ALL"を渡すことができ、他の列挙型は包含ではなく除外になることです。 「ALL」、「Ancient」は「Ancients以外のすべて」を意味します。これはある種の理にかなっていますが、明らかにUIは複雑すぎるものを反映する必要があります。
TL; DR;ほとんどの場合、「すべて」はUIの優れた点であり、サービスレベルで曖昧さなく同じことを実現できるため、実行しないでください。
入力に年号がない場合、この本はすべての年号をカバーすると見なされます。
これと、特殊な「ALL」ケースがあることは悪い見方です。APIは可能な限り明示する必要があります。 「実際には何も意味しない」のような特別なケースがあるということは、APIを使用する誰もがすべての特別なケースを知っている必要があることを意味します。
特殊なケースでは、ユーザー入力が有効であるかどうかの検証と、要求の適切な処理の両方に関して、多くの場合、コードがより複雑になります。
tl; dr
実際の質問–実装のローカルデータ構造に関して–私はあなたの結論に同意します:all erasの特別な値は避けてください。間違いをする。ただし、特にエンドユーザーのUIとシリアル化されたペイロードデータは異なるストーリーになる場合があります。
これを型システムの観点から見てみましょう。基本的に列挙型は、特定のカテゴリ(列挙子)の分離したセットを定義するタイプであり、すでに answer by @J H で指摘されています。列挙型の変数は、これらのカテゴリの1つだけを保持します。列挙子の値のコレクションを表す場合は、2番目のタイプが必要です。
// C++-inspired pseudo-code
enum Era { ancient, post_classical, modern };
using Eras = Collection<Era>;
all
列挙子は、これら2つのタイプを一緒にマッシュするため、実行できません。これは単一のカテゴリではなく、すべての可能なカテゴリのコレクションです。
厳密に実用的なレベルでは、あらゆる種類の特別な値を扱うと、実装が複雑になり、エラーが発生する可能性が高くなります。これは、あらゆる場所で特別なケースにするか、実際の意味と一致させるために展開する必要があるためです。ああ、そしてすべての可能な列挙子の明示的なコレクションはどうですか?これをいつ、どこで特別なall
値に縮小する必要がありますか?それはまったく折りたたまれるべきですか?
私の好ましいアーキテクチャは、コードにall erasの表現がないことです。これにはall
列挙子が含まれますが、空のコレクションはすべての時代を意味します。それはまったく同じ特別なケースですが、別の変装をしています。
リストの2つの項目が提供されている場合、「ALL」と「Ancient」と言います-アプリケーションから何が取得されますか? [...]
all erasマーカーは、それ自体の上に何かを置くことは意味がないため、常に単独で表示する必要があることをAPI仕様で指定します。次に、これを契約違反として拒否します。しかし、これはすべての時代が実装とビジネスロジックの両方を複雑にする方法の良い例です。
主な問題は、特別な値とその基本的な意味の間で変換するためのルールを指定する必要があることです。そして、あなたとAPIを使用する他のすべての人は、それらのルールを正しく実装する必要があります。私の中の皮肉屋は、それがif誰かがそれを間違ってしまうのではなく、単にwhenの問題ではないことを教えてくれます。
元々、ここには、変更クエリと読み取り専用クエリ、および"eras"
リストのさまざまな役割に関するセクション全体がありました。しかし、結局私はKISSだったのでそれを削除しました。 enum/collectionのルールはJSONデータにとって不当ではないため、物事の一貫性を保つことが最も簡単なソリューションです。
UIと基になるデータ構造の間にデータ変換があると思いますが、おそらく何らかのMVC風のアーキテクチャーがあるためです。したがって、ユーザーにとって最も便利で直感的なものを使用してください。 彼の回答@Markus は、曜日のさまざまな選択オプションの優れた例を示しています。
新しいALLを使用すると、まったく使用しない場合よりも混乱が生じると思います。
はい。
コレクションの観点から考えると、何かの完全なコレクションがあります。そして、そのコレクションのサブセットのみが必要な場合は常に、要素がこのサブセットの要素と見なされるときに、何らかのfilterconditionを提供する必要があります。そして、ALL
は、フィルターが適用されない場合であり、"filtercondition is ALL
"ではありません。
入力に年号がない場合、この本はすべての年号をカバーすると見なされます。
私はそれをひっくり返します:この本は特定なし時代をカバーします。
これはクエリの観点からも一貫しています:
年号が選択されていない場合、all本が返されます(これには空のeraフィールドが含まれます)。時代を選択すると、選択した年代の書籍のみが返されます-特別な時代と一般的な時代のすべての書籍を取得することは、どういうわけか予想外です。
ALL
-カテゴリを追加する唯一のポイントは、ユーザーの便宜のためです。これは、「すべて」ではなく「何も」を選択しないほうがイライラする可能性があるためです。
カテゴリー変数の場合、同じレベルにワイルドカード「all」を配置することは避けます。
期間について2つのレベルの検索基準があるようです:
呼び出し元は、(false、無視)または(true、{'ancient'、 'modern'})を指定できます。
または、空のセットをワイルドカードとして解釈するように選択することもできます。これは、非選択的であることを示します。
あなたの特定のケースでは、いくつかのよく知られた値に離散化された年である連続変数があり、本当に必要なのは区間演算を行うことです。したがって、クエリは(開始、終了)年の範囲を受け入れ、列挙型は便利にそのような属性を提供できます。
私は明示的にALL列挙型を運ぶというアイデアが好きですが、私はむしろ フラグとして設定します
const enum = {
ALL: { value: 0 },
ANCIENT: { name: 'Ancient', value: 1 },
POST_CLASSICAL: { name: 'Post Classical', value: 2 },
MODERN: { name: 'Modern', value: 4 }
}
flagon などのライブラリを使用するか、すべての値が選択されているときに bitwise operations で手動で再生する場合は、代わりにALLを使用します。
Enumの値は常に前の値の2倍でなければならないことに注意してください。そして、健全性チェックのために列挙型を削除してはいけませんが、それを無効にしても、無効にされたものを常にALLの一部としてカウントするようにコードを調整します。
代わりにNONEフラグを設定し、後でビジネスが変わった場合に備えて、NONEが使用されたときにクライアントが何をすべきかをクライアントに決定させます。
この実装が採用されている場合、列挙を渡す代わりに、選択された値の合計を渡します。
私は最近、基本的なスケジューラを実装しました。@ LoztInSpaceですでに述べたように、そのほとんどはUIシュガーです。
ユーザーインターフェイスは、ユーザーがオプションの1つを選択したときに何を達成するかを完全に明確にする必要があります。
私の場合、選択できる曜日があります。 「すべて」に加えて、「営業日」と「週末」をオプションとして追加しました。各オプションを選択すると、後で使用できるようになり、含めない日は手動で選択解除する必要があります。
技術的にはこれは、ユーザーが「平日」を選択した場合、UIには5つのチェックボックスがオンになり、列挙型は「稼働日」の値を使用することを意味します。チェックボックスの1つがオフの場合、「稼働日」の値は残りの4日間のoredビットマップに置き換えられます。
オプションの一部を使用してすべてを含めたり、同時に何かを除外して競合を回避したり、UIがユーザーにとって意味のあるものであることを確認したりしないでください。
ユーザーが「すべて」に含まれるものの概念を簡単に把握できる場合(週のすべての曜日)、またはそれほど重要ではない場合(Amazonのすべてのカテゴリを検索)、「すべて」オプションを使用することは意味があると思います。