Skip to main content

Development Best Practices

Defining the name of your usecase​

The name of the usecase must be something that refers to the business, making its real purpose clear to the business team.

Good descriptions:

✅ Search one or several funds

✅ Filter users by document

✅ Update unit price

✅ Find the funds in Bank

Bad descriptions:

❌ list the information

❌ Calculates the percentage of the fund

❌ calculates the total

❌ Save user in bank

❌ Update Client

❌ Starts the services and returns the JobId

Reference: https://herbsjs.org/docs/usecase/features/#creating-a-use-case

Creating custom methods in the repository​

To create a non-standard / custom query for your repository, we can call knex directly, without the need to create a new variable and call it in the constructor.

Don't: create a variable to receive the connection

// Don't

class UserRepository extends Repository {
constructor(injection) {
super({
entity: User,
table: "users",
knex: connection
})

this.conn = connection
}

async getUserByNickName(nickname) {
const ret = await this.conn('users').select('*')
.where('nickname', nickname)

if (!ret[0] || ret[0].length == 0) return undefined
return User.fromJSON(ret[0])
}
}

Correct: Use this.knex inside your methods

Call this.knex directly in our methods

// Do

class UserRepository extends Repository {
constructor(injection) {
super({
entity: User,
table: "users",
knex: connection
})
}

async getUserByNickName(nickname) {
const ret = await this.knex('users').select('*')
.where('nickname', nickname)

if (!ret[0] || ret[0].length == 0) return undefined
return User.fromJSON(ret[0])
}
}

Handling usecase failures​

To carry out the treatment of errors that occurred during the execution of the usecase, enable the environment variable HERBS_EXCEPTION in the production environments so that Herbs can obfuscate the errors generated and handle it on its return.

// Don't
const createProduct = injection =>
usecase('Create Product', {

request: {
name: String,
...
},

response: {
product: Product
},

'Check if the Product is valid': step(ctx => {
try {
if (!isValid) return Err(errors)
return Ok()
}
catch(e) {
throw new "Error to validated product"
}
}),

'Save the Product to the repository': step(async ctx => {
try {
ctx.ret.product = await repo.insert(product)
return Ok()
}
catch(e) {
throw new "Error to save product"
}
}),
// Do

HERBS_EXCEPTION = audit

const createProduct = injection =>
usecase('Create Product', {

request: {
name: String,
...
},

response: {
product: Product
},

'Check if the Product is valid': step(ctx => {
if (!isValid) return Err(errors)
return Ok()
}),

'Save the Product to the repository': step(async ctx => {
ctx.ret.product = await repo.insert(product)
return Ok()
}),

Creating the business rules workflow​

Use IfElse to direct your flow of business rules, making your code easier to understand and document.

// Don't

const updateItem = (injection) =>
usecase('Update Task', {

...

'Check if it is necessary to update Task positions': step(ctx => {
if(ctx.hasChangedPosition)
...
else
...
}),

}),
// Do

const updateItem = (injection) =>
usecase('Update Task', {

...

'Check if is necessary to update Task positions': ifElse({

'If position has changed': step(ctx => {
return Ok(ctx.hasChangedPosition) // true or false
}),

'Then rearrange positions and save Tasks on repository': step(async ctx => {
...
return Ok()
}),

'Else save updated Task on repository': step(async ctx => {
...
return Ok()
}),

}),

Creating know errors​

User Known Errors to expose runtime metadata. With this runtime information the glues like Herbs2REST or Herbs2GraphQL are able to change the API return.

// Don't
const createProduct = injection => usecase('Create Product', {
'Check if the Product is valid': step(ctx => {
...
if (!isValid) throw 'Product not found'
}),
// Do
const createProduct = injection => usecase('Create Product', {
'Check if the Product is valid': step(ctx => {
...
if (!isValid) {
const options = { message: Product ID ${id} not found, payload: { entity: 'product' } }
return Err.notFound(options) // return an Err with "not found" code
}
}),

To learn more about know errors, you can open this link known-errors