Common components
In general, APIs should be designed to be self-contained. APIs generally need to be able to move forward independently of one another, and mutual dependencies can cause downstream APIs to be forced into taking major version changes or even lead to dependency conflicts.
However, there are also cases where common structures are valuable, especially where a concept is well-known and it is sufficiently clear that it will not change. Having a single representation of these common structures is valuable because it avoids disrepancies between APIs in things like how dates or monetary values are represented. It also enables shared libraries for common operations, such as basic arithmetic on monetary values.
Common components serve this use case.
Guidance
The public representation of APIs should be self-contained (for protocol
buffers, this means that all API protos used by the API originate in the same
proto package
), except for common components, which may be used freely in
any API.
An API must not define a set of API-specific common components which live outside of its versioning structure. This prevents independent movement of particular versions and also causes problems for client libraries in many languages that compile protobuf messages into classes.
An API should not define alternative representations of any of the existing common components described below, even within its versioning structure.
Existing common components
The common components, which public-facing APIs may safely depend on, are
defined canonically in the AEP common components repository. These include
READMEs with canonical definitions for each component, and — when applicable
— implementations of these definitions, in both JSON Schema and protobuf
formats. The protobufs are also published to the Buf Schema Sepository at
buf.build/aep
, and the JSON schemas are published to the JSON Schema
Store with names beginning with aep-
, for example aep-type-money
or
aep-longrunning-operation
.
While the AEP common components repository is canonical and takes precedence over this list, some of the common components defined there include representations of the following concepts:
API design patterns
Operation
: Represents the status of a long-running request (see AEP-151 for details).
gRPC-specific API design patterns
-
google.api.*
(but not subpackages ofgoogle.api
): Annotations useful for gRPC/JSON transcoding, supported by frameworks including .NET 7. -
google.rpc.*
: A small number of components related to gRPC request/response status and errors.
Common types
This section provides examples for the sake of illustration; it may not be exhaustive. The AEP common components repo is canonical.
General common types
-
Color
: RGB or RGBA colors. -
Decimal
: Decimal numbers. -
Fraction
: A numeric fraction. -
LatLng
: Geographic coordinates. -
Money
: An amount of money in a given currency. -
PhoneNumber
: A phone number in most countries. -
PostalAddress
: Postal addresses in most countries. -
Quaternion
: A geometric quaternion.
Date- and time-related types
-
Date
: A calendar date, with no time or time zone component. -
DateTime
: A calendar date and wall-clock time, with optional time zone or UTC offset information. -
DayOfWeek
: An enumeration representing the day of the week. -
Duration
: A duration with nanosecond-level precision. -
Interval
: An interval between two timestamps. -
Month
: An enumeration representing the Gregorian month. -
TimeOfDay
: Wall-clock time, with no date or time zone component. -
Timestamp
: A timestamp with nanosecond-level precision.
Protobuf well-known types
The google.protobuf
package is shipped with protocol buffers
itself, rather than with API tooling. The Well-Known Types defined in this
package should always be used when appropriate, and the AEP common
components repo does not define any protos for these types, even when it
defines a corresponding JSON Schema. These include:
google.protobuf.Duration
: Durations, with nanosecond-level precision. The protobuf runtime provides helper functions to convert to and from language-native duration objects where applicable (such as Python’stimedelta
).google.protobuf.Timestamp
: Timestamps, with nanosecond-level precision. The protobuf runtime provides helper functions in most languages to convert to and from language-native timestamp objects (such as Python’sdatetime
).
google.protobuf
also provides some useful components that correspond to JSON
primitives (and so have no representation at all in the AEP common
components repo), namely:
google.protobuf.Value
: An arbitrary JSON value. The protobuf runtime provides helper functions in most languages to convertValue
objects to and from JSON.google.protobuf.Struct
: JSON-like structures (a dictionary of primitives, lists, and other dictionaries). The protobuf runtime provides helper functions in most languages to convertStruct
objects to and from JSON.
google.protobuf.Struct
and google.protobuf.Value
are designated common
components by this AEP; proto-based APIs should use them when representing
arbitrary JSON-like structures.
Libraries for common types
For the common components in the aep.type
namespace, which represent common
data types, the AEP common components repo may contain canonical libraries
in a number of languages. These libraries are designed to be idiomatic in a
given language, and should feel similar to using the language’s standard
libraries. They should provide basic functionality like adding two Money
values, or determining if one Date
comes before another.
When a language already has a standard library representation of a common type
(such as Python’s datetime
for the Timestamp
type), there may instead be a
library for converting the JSON or protobuf representation to the standard
library representation.
If a library you want does not exist, and you want to contribute one, please open an issue on the AEP common components repository in GitHub.
Appendix: Adding to common components
Occasionally, it may be useful to add protos to these packages or to add to the list of common components. In order to do this, open an issue on the AEP common components repository in GitHub.
However, some general guidelines are worth noting for this:
- Schemas should only be granted common component status if it is certain that they will never change (at all — even in ways that would normally be considered backwards compatible). Common components are not versioned, and it must be the case that API creators and consumers can rely on the component to be a complete and accurate representation indefinitely.
- Schemas must be applicable to a significant number of APIs for consideration as common components.
- Even after a common component is added, APIs using local versions must continue to do so until they go to the next major version.