HATEOAS REST API and Domain Driven Design, where to put the workflow logic?
Asked Answered
T

3

8

This is intended as a follow up question to RESTful API: Where should I code my workflow? A brief summary of the question (adapted to fit my question a bit better) would be something like this:

Each domain object contains business logic associated with the specific object in a certain bounded context (X). The REST API contains logic to transform the result of a query or command into data sent over the wire (JSON for example). When using HATEOAS and hypermedia we want to model relationships between resources using links. But in order to determine which links that should be returned by the REST API one often need to resort to business logic / rules. The question is, where do these "workflow rules" belong in a DDD application? Would they perhaps be in a different bounded context dealing only with the workflow rules (perhaps in a "partner"-like relationship with X) or would they belong in the X BC?

Tsarevna answered 24/5, 2015 at 14:50 Comment(3)
Do you have a concrete example on what kind of business logic is needed to resolve links?Semiconscious
Perhaps not concrete but one example that I can think of off the top of my head would be authorization issues. For example user U may only be authorized to read and update resource R but not delete it. In these cases a link to delete should not be included in the response. Another example would be a VIP customer that is given "additional links" compared to a non VIP customer.Tsarevna
A good example may be an ecommerce application that does allow you to add something to your cart because it is out of stock.Fuller
F
1

I'm not a DDD expert (only through 100 pages in Eric Evan's book) but I can tell you what happened in our case of a e-commerce catalog.

Initially we had such business flow in the same bounded context of the app based on data and the requesting user's roles/permissions we altered the state transitions (you said links, but this is really the heart of it, links are just one way to present state transitions) presented to the user. This worked ok, but it was a lot of repeated logic being executed. Then we added a search application that was a different bounded context as it presented the same data but in different collections/groupings and we didn't want to have them get out of sync.

We moved to a CQRS implementation, so a lot of our business logic is "pre-calculated" thus it's in something like a "partner context" as part of our projection from write model to read model. We don't store the links specifically, but instead flag allowed behaviours on the data. Both the catalog application and search application use this read model and it's behaviour flags to determine state transitions to present to the client.

Some stuff happens on the fly upon requesting of the resource, almost at the serialization step. We've targeted these to be moved to the pre-calculated as much as possible, but what we can't precalculate (only because of the scale) is stuff that is based specifically on the user. Like the recommended search which uses BI data within the search engine to return results. We could pre-calculate that for every single user, but the numbers are too huge for our systems right now. So we send the main apps calculated resource (from the main context) and pass it through yet another partner context to further refine things.

another use case is some links are only presented to authenticated user, and thus are hidden from anonymous users. We do this in the main apps context but it's starting to be a bit of a hindrance because their presence indicates what the user behind the request can do in other apps (like change password), and our context isn't really the authority of what the user can do in some other app. It would be nicer to hand the resource off to their context and have them process it before we return it to the user. One simple solution we used for this was instead of deep linking to functions within the external context, we link to a root resource of that context and allow it present state transitions. It means there's 2 requests, but it cleans up the locations/authorities of the logic.

Fuller answered 26/5, 2015 at 23:10 Comment(0)
C
0

The workflow is actually composed of domain knowledge and hateoas infrastructure.

If the application is not designed with hypermedia, you may not even need the hateoas part. But the domain knowledge still exists, they just live in the end users' minds. You need domain knowledge to validate the user's command just in case they forget it.

So for hateoas, I split workflow implementation into two parts: Domain models and hateoas infrastructure.

The domain models tell the domain knowledge.
The hateoas infrastructure, on the other side, is hateoas specific, put them out of the domain.

Here is a java example, using springframework-hateoas. I put the workflow in the procedure of mapping domain models to resources.

@Component
public class OrderResourceAssembler extends ResourceAssemblerSupport<Order, OrderResource> {

    @Override
    public OrderResource toResource(Order model) {

        OrderResource resource = mapToResource(model);
        resource.add(orderResourceHref(model).withSelfRel());//add self link
        if (model.isAvailableToCancel()) { //let the model tell
            //add cancel link
            resource.add(orderResourceHref(model).withRel("cancel")); 
        }
        if (model.isAvailableToPay()) { //let the model tell
            //add payment link
            resource.add(new Link("http://www.restfriedchicken.com/online-txn/" + model.getTrackingId(), "payment")); 
        }
        //etc


        return resource;
    }
    // omitted codes
}

If the model cannot tell on its own, you may introduce specification object to do the job.

Cornflower answered 27/5, 2015 at 9:55 Comment(0)
V
0

Wherever the resource resides is the same place that needs to marshal the access to it.

Say you've got a larger system with three bounded contexts: employee management, customer intake, and service provisioning.

GET an employee's information from employee management? Deciding whether to show the link that can be used to POST or DELETE a role is for employee management to decide.

GET a freshly submitted service contract from customer intake? Deciding whether to show the link that can be used to PUT up a revision or POST follow up details is for customer intake to decide. Sure, employee management presides over the handing out of the roles necessary to work with new service contracts, but customer intake is responsible for knowing which claims and other conditions are required, and will ultimately ensure that everything checks out.

Likewise, GET a physical installation's software profile from service provisioning? Service provisioning is going to need to make sure that everything is in order with other services/contexts (customer's account paid up, current user in the network services role, device not locked out for maintenance) before it gives out the link where you can PUT the new quality of service specs.

These kind of 'workflow' decisions are integral to each context -- they should not be split out elsewhere.

Vivacious answered 10/6, 2015 at 1:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.