0

I have a gradle multiproject with 2 subprojects trying to emulate an hexagonal architecture :

  1. rest-adapter
  2. application layer

I don't want the application services to expose the domain models and do'nt want to force a specific representation as output. So I would like something like application services consume 2 args (a command and something) and return a T. The client configures the service.

The rest adapter doesn't ave access to the domain model, so I can't return the domain models and let the adapter creates its representation.

What about the something. I tried :

  1. have a signature <T> List<T> myUseCase(Command c, Function<MyDomainModel, T> fn). The application layer is the owner of transformations functions (because the signature uses MyDomainModel) and exposes a dictionnary of function. So the rest controller references one of the Fn. It works. And I'm searching of a better way. More elegant way if it exists.
  2. have a signature <T> List<T> myUseCase(Command c, FnEnum fn) For each enum I have associated a Function. With this, I found the signature more elegant : the consumer provides which transformation it wants from an enum. But doesn't work cause the generic method doesn't compile. The cannot be resolved. Currently, I didn't find a way.
  3. something with java 8 consumer or supplier or something else but I failed to wrap my head around.

I'm feeling there's a more elegant solution for this kind of problem : a service which accepts a function that transforms and build an output that the client provides.

Archange
  • 397
  • 5
  • 21

2 Answers2

0

I'm feeling there's a more elegant solution for this kind of problem : a service which accepts a function that transforms and build an output that the client provides.

You are sending data across the boundary between the application and the REST layer (and presumably between the application and the REST consumer); it may be useful to think about messaging patterns.

For example, the application can define a service provider interface that defines a contract/protocol for accepting data from the application.

interface ResponseBuilder {...}

void myUseCase(Command c, ResponseBuilder builder)

The REST adapter provides an implementation of the ResponseBuilder that can take the inputs and generate some useful data structure from them.

The response builder semantics (the names of the functions in the interface) might be drawn from the domain model, but the arguments will normally be either primitives or other message types.

CQS would imply that a query should return a value; so in that case you might prefer something like

interface ResponseBuilder<T> {
    ...
    T build();
}

<T> T myUseCase(Command c, ResponseBuilder<T> builder)

If you look carefully, you'll see that there's no magic here; we've simply switched from having a direct coupling between the application and the adapter to having an indirect coupling with the contract.

EDIT

My first solution is using a Function<MyDomainModel, T> which is a bit different from your ResponseBuilder ; but in the same vein.

It's almost dual to it. You'd probably be a little bit better off with a less restrictive signature on myUseCase

<T> 
List<T> myUseCase(Command c, Function<? super MyDomainModel, T> fn)

The dependency structure is essentially the same -- the only real difference is what the REST adapter is coupled to. If you think the domain model is stable, and the output representations are going to change a lot, then the function approach gives you the stable API.

I suspect that you will find, however, that the output representations stabilize long before the domain model does, in which case the ResponseBuilder approach will be the more stable choice.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • Thanks. My first solution is using a Function which is a bit different from your ResponseBuilder ; but in the same vein. The ResponseBuilder signature is the same as a `Supplier : () -> T` I failed to implement my contract using a Supplier. I will try again :D – Archange Apr 29 '18 at 15:56
0

I think that what you need to implement is the so called "Data Transformer" pattern.

Imagine that you have a use case that returns a certain domain object (for example "User"), but you shouldn't expose domain to clients. And you want every client to choose the format of the returned data.

So you define a data transformer interface for the domain object:

public interface UserDataTransformer {

    public void write ( User user );

    public String read();

}

For every output format your clients need you define a class implementing the interface. For example if you want to represent the User in XML format:

public class UserXMLDataTransformer implements UserDataTransformer {

    private String xmlUser;

    @Override
    public void write(User user) {
        this.xmlUser = xmlEncode ( user );
    }

    private String xmlEncode(User user) {
        String xml = << transform user to xml format >>;
        return xml;
    }

    @Override
    public String read() {
        return this.xmlUser;
    }

}

Then you make your application service depends on the data trasnsformer interface, you inject it in the constructor:

public class UserApplicationService {

    private UserDataTransformer userDataTransformer;

    public UserApplicationService ( UserDataTransformer userDataTransformer ) {
        this.userDataTransformer = userDataTransformer;
    }

    public void myUseCase ( Command c ) {
        User user = << call the business logic of the domain and construct the user object you wanna return >> ;
        this.userDataTransformer.write(user);
    }

}

And finally, the client could look something like this:

public class XMLClient {

    public static void main ( String[] args ) {

        UserDataTransformer userDataTransformer = new UserXMLDataTransformer();
        UserApplicationService userService = new UserApplicationService(userDataTransformer);
        Command c = << data input needed by the use case >>;
        userService.myUseCase(c);
        String xmlUser = userDataTransformer.read();
        System.out.println(xmlUser);
    }

}

I've consider that the output is a String, but you could use generics maybe to return any type you want.

I haven't mentioned it, but this approach injecting the transformer into the application service follows the "port and adapters" pattern. The transformer interface would be the port, and every class implementing it would be an adapter for the desired format.

Also, this was just an example. You can use a dependency injection framework like Spring in order to create the component instances and wire them all. And also you should use the composition root pattern to do it.

Hope this example helped.

choquero70
  • 4,470
  • 2
  • 28
  • 48
  • Thanks. That's the pattern I've read from IDDD of Vaugh Vernon. I didn't find a way to write it using Java 8 (avoiding an interface, ...) for something more light to write / read / maintain ? – Archange May 01 '18 at 06:14
  • I will search if Supplier can fit or Kotlin extension functions. Any others ideas ? – Archange May 01 '18 at 06:39
  • @Archange I've never used Java 8 Supplier. I've used Java 9 ServiceLoader and providers. Maybe what you are looking for is to pass the provider to the use case? You would have the data transformation service, and a provider for each output format. The client instantiates the desired provider using the service loader, and pass it to the use case. But in the end is the same, the data transformation service is an interface implemented by the providers. – choquero70 May 01 '18 at 11:50