0

I've been studying Clean Architecture, Hexagonal Architecture, and implementing pieces I find applicable to my application. I know both architectures talk about separating your domain from the outside world. All of the resources I've read use controllers, CLIs, repositories, and communication platforms like Twilio as examples of adapters. I'm using a lib for generating JWTs (JSON Web Token). My question is if the implementation for the lib belongs in the domain layer or outside of it. I only ask because all of the resources I've read never mention libs like validation, JWTs, and generating UUIDs in the outside layer. It's usually the stuff I've listed above like controllers and databases outside of the domain.

  • I'd say this *should* be a non-issue because these should always be managed by your language's dependency management tool, eg if you're working with Typescript that would be npm or Yarn. – Matthew Daly Oct 06 '21 at 09:50
  • I solved the issue that I needed to create UUID objects and add validation on Entities doing these things below. As an example, I'll quote the UUID generation: 1) I create a folder named "shared" on the root of the project. I added a IdFactoryProvider inside with a IdFactory as a static private attribute. I added two static methods inside: get (to return the factory) and changeImplementation (to be used only on test env. Internally it throws if it's not test env). 2) On infra layer I created a IdFactory protocol and the implementation UuidIdFactory, that calls uuid library internally. – Vinicius Carvalho May 08 '22 at 21:35

1 Answers1

1

As you mentioned Hexagonal Architecture cares about outside communication and tools like validation, JWTs, and generating UUIDs are regarded as part of the core application (Where the core is the internal logic of the app).

Regarding Clean Architecture when using 3rd party libs you should expose them using your own interface instead of directly using theirs.

Bad

//myService.js
class MyService {
    
    constructor(uuid){
        this.uuid = uuid;
    }

    save(...) {
        const id = this.uuid.v4(); // <--- hard to refactore if "uuid" will change its API
        ...
    }

}

//app.js
const uuid = require('uuid');

const MyService = require('./myService');
...
const FooService = require('./FooService');


const myService = new MyService(uuid);
...
const fooService = new FooService(uuid);



Good

//uuidFactoryProvider.js
function uuidFactoryProvider(uuid){
    return function uuidFactory() {
        return this.uuid.v4(); // <-- easy to refactore
    }
}

//myService.js
class MyService {
    
    constructor(uuidFactory){
        this.uuidFactory = uuidFactory;
    }

    save(...) {
        const id = uuidFactory();
        ...
    }

}

//app.js
const uuid = require('uuid');

const uuidFactoryProvider = require('./uuidFactoryProvider');
const MyService = require('./myService');
...
const FooService = require('./FooService');


const uuidFactory = uuidFactoryProvider(uuid);
const myService = new MyService(uuidFactory);
...
const fooService = new FooService(uuidFactory);

By wrapping the 3rd party libs with your own API the codebase will have time minimal dependency on the libs API.

Daniel
  • 2,288
  • 1
  • 14
  • 22
  • Thanks. As you mentioned, you would put libs in the domain. I noticed you're passing `uuidFactory` into the constructor, which I can kind of understand why. I'm curious why pass `uuidFactory` into the constructor if it's part of the domain instead of importing it from inside `myService.js`. CA mentions using dependency inversion to define a contract for interacting with the outer layers. `uuidFactory` isn't something that'll be replaced by a mock implementation for testing like you would do for a database or a 3rd API you're calling, so I'm wondering why to pass it in that way. @Daniel –  Oct 06 '21 at 11:09
  • Actually, I would mock `uuidFactory` if writing **unit-tests** for `myService`. That's why I inject everything (services, factory, and values). Yes, it's a bit over the top I know, but I learn the hard way that using 3rd API can come back and bite you :) – Daniel Oct 06 '21 at 14:41