How can one prevent their model classes getting too large or depending on objects not directly related to them?
Example
Imagine a user registration form, that once successfully registered, the user receives an email to verify their email address.
Sample code 1
disclaimer - this is demonstration code
// route.js
app.post('/register', (req, res) => {
container.get('UserController').postRegisterUser(req, res);
});
Option 1 - adding emailSender dependency inside the User model
// userController.js
class UserController {
public constructor(model) {
this.model = model;
}
public postRegisterUser(req, res) {
const {email, password, repeatPassword} = req.body;
const user = this.model.registerUser({email, password, repeatPassword});
return res.render('/', user);
}
}
// UserModel.ts
class UserModel {
constructor(repository, validator, emailSender) {
this.repository = repository;
this.validator = validator;
this.emailSender = emailSender; // <--- emailSender injected in to model
}
public registerUser(user) {
if (this.validator.isValid(user)) {
this.repository.save(user);
this.emailSender.sendVerificationEmail(user.email); // <---- emailSender called here
return user;
}
}
}
Option 2 - adding emailSender dependency inside the controller
// userController.js
class UserController {
public constructor(model, emailSender) {
this.model = model;
this.emailSender = emailSender; // <--- Injected in constructor
}
public postRegisterUser(req, res) {
const {email, password, repeatPassword} = req.body;
const user = this.model.registerUser({email, password, repeatPassword});
this.emailSender.sendVerificationEmail(email); // <--- emailSender called here
return res.render('/', user);
}
}
// UserModel.ts
// no EmailSender injected
class UserModel {
constructor(repository, validator) {
this.repository = repository;
this.validator = validator;
}
public registerUser(user) {
if (this.validator.isValid(user)) {
this.repository.save(user);
return user;
}
}
}
One can see how large these dependencies can become when other systems become involved - such as sending data to an S3 bucket, generating a csv file, etc. I ideally don't want my controller or model to have these dependencies, but then where do they go?
I imagine the ideal solution is something in-between these two options outlined above. I am just having difficulty with how to implement my web application.
You can build another component UserProcessor
or UserRegistrationProcessor
, which contains all those external calls to other services, and inject that component into UserController
.