0

I am creating an API using HAPI and Joi to validate inputs and I am having troubles with sharing my validation schema between different modules. I am using a component-oriented architecture that looks like that

components
|_ moduleA
   |_ moduleAController
   |_ moduleAModel
   |_ moduleARoute
   |_ moduleAValidate
|_ moduleB
   |_ moduleBController
   |_ moduleBModel
   |_ moduleBRoute
   |_ moduleBValidate
|_ moduleC
...

In each module, moduleXRoute create a route associating a handler from moduleXController and a validator from moduleXValidate.

As I am using Joi, I am performing some tests on input data and that where comes the problem, my moduleA keeps a list of moduleB and my moduleB keeps a reference to moduleA, thus this implies in the validators :

var moduleASchema = {
    _id: Joi.objectId(),
    name: Joi.string().required(),
    moduleB: Joi.array().items(Joi.alternatives().try(Joi.objectId(), moduleBSchema)),
};

var moduleBSchema = {
        _id: Joi.objectId(),
        name: Joi.string().required(),
        moduleA: Joi.alternatives().try(Joi.objectId(), moduleASchema),
};

That's why, I think it would be a good idea that moduleAValidate and moduleBValidate expose moduleASchema and moduleBSchema that other modules could use.

The problem is that it makes a circular dependency problem because in the case above, I would have:

//moduleAValidate.js
var moduleBSchema = require('../moduleBValidate').moduleBschema;

//moduleBValidate.js
var moduleASchema = require('../moduleAValidate').moduleAschema;

Thus, what would be the good way to handle the problem ?

I found the easy way would be to centralize all schemas in a single file that could be required in all validators, but I feel it like it would just be in contradiction with component-architecture.

ChrisV
  • 233
  • 4
  • 15

2 Answers2

0

Best way would be to have a central point where common parts of schemas are kept in a central place and loaded into the required schemas when needed.

simon-p-r
  • 3,623
  • 2
  • 20
  • 35
0

I finally decided to solve the chicken-and-egg problem by using dependency injection. In the above example, I would do something like

//moduleBValidate.js
var moduleBSchema = new (require('moduleBSchema.js'))();
var moduleASchema = new (require('moduleASchema.js'))(moduleBSchema); // Here I use dependency injection for injecting moduleBSchema in moduleASchema

//moduleAValidate.js
var moduleASchema = new (require('moduleASchema.js'))();
var moduleBSchema = new (require('moduleBSchema.js'))(moduleASchema);

And I built the joi schema this way:

function moduleASchema(moduleBSchema, moduleCSchema...) { // Here we put all dependencies of moduleASChema
    moduleBSchema = moduleBSchema || Joi.object();
    moduleCSchema = moduleCSchema || Joi.object();
    ...

    this.schema = {
        name: Joi.string().required(),
        moduleBRef: Joi.alternatives().try(Joi.objectId(), moduleBSchema),
        moduleCRef: Joi.alternatives().try(Joi.objectId(), moduleCSchema)
    };
    return this.schema;
}

module.exports = SiteSchema;
ChrisV
  • 233
  • 4
  • 15