Separating by call semantics in the real world

There is lots of great advice on designing message based service APIs here including this:

Group by Call Semantics and Response Types…Every property in each Request DTO has the same semantics that is for SearchProducts each property acts like a Filter (e.g. an AND) whilst in GetProduct it acts like a combinator (e.g. an OR).

Sometime this seems easier said than done. For example, consider an API that gets a grade for a particular student in a particular class and we’d like to select the student either by ID number or by name:

class GetGrade
{
  int? id;
  string name;
  int? class_id;
}

But here the semantics are mixed: (id OR name) AND class_id. We could make it more complex by specifying a specific term_id as well. How to better design this interface? In general what to do when you want multiple flexible selection methods for some criteria AND you need to specify additional filter parameters? Alternately how to better document the interface semantics?

Also the design guidance says:

Keep a consistent Nomenclature
You should reserve the word Get on services which query on unique or Primary Keys fields, i.e. when a supplied value matches a field (e.g. Id) it only Gets 1 result. For “Search Services” that acts like a filter and returns multiple matching results which falls within a desired range we recommend using prefixing Services with the Search or Find verbs to signal the behavior of the Service.

But what about naming for cases where I expect the service to return exactly 1 result, but I must specify multiple filter attributes to get that one result (instead of a PK or uniquely constrained value)?

If the requests contains fields that are never used together, e.g. id + name, it’s better to create them in different requests so you can make all fields required which indicates how each Service needs to be called, e.g:

[Route("/grades/{Id}/{ClassId}")]
class GetGrade
{
    int Id;
    int ClassId;
}

[Route("/grades/by-name/{Name}/{ClassId}")]
class GetGradeByName
{
    string Name;
    int ClassId;
}

You could also have them share the same path info (i.e. differentiated by int/string) by using a Custom Route Rule.

This is still an AND filter, if it requires multiple filters to return a single result it indicates if the request is missing all filters the query is going to return multiple results. If your Response DTO only allows for 1 Result then you’re going to throw an Error if the query returns 0 or more than 1 result.

1 Like