I'm prototyping out an application here where I'm using domain-driven design to identify the domain(s) of the application and I've ended up with something that - at least so far - feels like a fairly decent architecture where each bounded context in my domain is structured as its own hexagonal application. Data flows between each bounded context primarily using domain events as the published language, with adapters in the consuming BC translating relevant events from publishing BCs into domain specific language. To have some solid examples to use in this question, I've chosen to use the BCs Vaughn Vernon's Implementing Domain-Driven Design book where they end up identifying three separate bounded contexts:
- Identity & access
- Collaboration
- Agile product management
These are not my specific bounded contexts but I chose to use these as examples here as they might be more familiar to whoever is reading this. On top of this, the plan is to implement a public RESTful API that spans the system as a whole - a unified API surface across all our internal bounded context for our entire domain. This is where I'm having a hard time finding a solution that feels just right as to where the implementation of the REST API should live and how it should integrate with my domain in a way that doesn't introduce needless coupling or a structural mess. The things I've been thinking about and evaluating so far are:
- Each BC has relevant HTTP handlers for its domain implemented as adapters. This is problematic for many reasons:
- It leads too quickly to exposing our domain model over REST which I'd want to avoid. I want to set up an architecture where it's encouraged to think of these as the two separate things that they are. Relevant reading/watching for this would be this stackoverflow question, DDD & Rest - Domain-Driven APIs for the Web by Oliver Gierke and Domain-Driven Design for RESTful systems by Jim Webber.
- Our REST API will be hypermedia-driven (HATEOAS), and every resource requested will contain information to the client about what it can do next - both where to find additional related information, and which operations it can perform on the resource it just requested. I'm worried this will introduce unnecessary coupling where an adapter in the agile product management context will have to know what URL an adapter in the collaboration context is using for its resources. As an example, when requesting a sprint resource, one of the links on that resource should be to the discussion for the sprint which is served by an adapter in a different bounded context.
- The REST API is its own bounded context. While this might make more sense as the REST API might have its own ubiquitous language to some extent, it also feels challenging to figure out where to draw the line here.
- Should the API BC use application services in the upstream BCs to provide the API with the data it requires?
- Should the API BC be as loosely coupled from the upstream BC as possible and have adapters consuming events from upstream BCs, translating them into read models that are tailored for the resources that are exposed in the API?
- The REST API is simply a client of the domain, and not represented as a bounded context on its own; it's simply a client of our bounded contexts, using the public API of each upstream bounded context to provide the functionality it needs. While this is fairly similar to #2, it would not necessarily be structured as a hexagonal application but instead just a semi-flat list of HTTP handlers/controllers with no architectural constraints on them outside of that.
Right now I think I'm leaning mostly towards #3, but I think I'm forcing myself there just because the other options have too many open questions with unclear answers - it might not be the ideal route to go. #2 is also an option but I struggle to decide where to draw the line on how it should integrate against its upstream BCs. #1 feels flat out wrong for the reasons I already outlined, but I wanted to include it for completeness.
What I'm looking for here is some input on the way I'm reasoning about this and things that I've missed or are just flat out wrong about. The "right choice" here is likely the one that fits our (the team that will write and maintain this) needs and requirements, and that no option is clearly superior in a general sense but it would be interesting none the less to get some thoughts and ideas around this.