diesis

Solve code complexity. Easily

Goals:

Keep the code "familiar"

Easiness

Remove code complexity

Simplicity

Easy

adjacent = lie near


const aFunction = (arg1, arg2) => {
  // return something
}

Our building block

You need an higher level abstraction

For your application

Dependency: a higher level abstraction


const { dependency } = require('diesis')

const aDependency = dependency([getArg1, getArg2], (arg1, arg2) => {
  // return something
})

const res = await aDependency()

const getGarlic = dependency(() => {/* ... */})
const getOliveOil = dependency(() => {/* ... */})

const getWater = dependency(() => {/* ... */})
const getSalt = dependency(() => {/* ... */})
const getDriedPasta = dependency(() => {/* ... */})

const getCondiments = dependency([getGarlic, getOliveOil],
    (garlic, oliveOil) => {/* ... */})

const getPasta = dependency([getDriedPasta, getWater, getSalt],
    (driedPasta, water, salt) => {/* ... */})

const pastaAglioOlio = dependency([getCondiments, getPasta],
    (condiments, pasta) => {/* ... */})

Pasta aglio and olio


const dish = await pastaAglioOlio()

Sort out execution order

Ensure a dependency is only executed once

Deal with sync/async

What diesis does:


const aDependency = dependency(['local', getDep], (local, dep) => {/* ... */})

Injecting arguments


await aDependency({ local: 3 })

const getGarlic = dependency(() => {/* ... */})
const getOliveOil = dependency(['servings'], (servings) => {/* ... */})

const getWater = dependency(() => {/* ... */})
const getSalt = dependency(() => {/* ... */})
const getDriedPasta = dependency(['servings'], (servings) => {/* ... */})

const getCondiments = dependency([getGarlic, getOliveOil],
    (garlic, oliveOil) => {/* ... */})

const getPasta = dependency([getDriedPasta, getWater, getSalt],
    (driedPasta, water, salt) => {/* ... */})

const pastaAglioOlio = dependency([getCondiments, getPasta],
    (condiments, pasta) => {/* ... */})

Pasta aglio and olio for n servings


const dish = await pastaAglioOlio({ servings: 3 })

diesis design

  • Low cognitive load
  • no "framework"
  • no centralised registry
  • no reimplementing language features

Simple

sim – plex = one braid

Code complexity explained with pasta

Spaghetti

you pull one, you get too many

Lasagne

To find what you search you have to dig through many layers

Copy/pasta

Solving problems adding more pasta

const numberOfVegetarianDishes = await countVegetarianDishes({ menuId: 123456 })

API: first iteration


const getDishes = (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
}

const countVegetarianDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.filter((dish) =>
    dish.vegetarian).length
}

Without Diesis


const getDishes = dependency(['menuId'], (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
})

const countVegetarianDishes = dependency([getDishes], (dishes) =>
  dishes.filter((dish) => dish.vegetarian).length)

With Diesis


const numberOfVegetarianDishes = await countVegetarianDishes({ menuId: 123456 })

API: Second iteration


const numberOfDishes = await countDishes({ menuId: 123456 })

const getDishes = (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
}

const countVegetarianDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.filter((dish) =>
    dish.vegetarian).length
}

const countDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.length
}

Without Diesis


const getDishes = dependency(['menuId'], (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
})

const countVegetarianDishes = dependency([getDishes], (dishes) =>
  dishes.filter((dish) => dish.vegetarian).length)

const countDishes = dependency([getDishes], (dishes) => {
  return dishes.length
})

With Diesis

API: third iteration


const numberOfVegetarianDishes = await countVegetarianDishes({ menuId: 123456 })

const numberOfDishes = await countDishes({ menuId: 123456 })

const vegetarianDishesRatio = await getVegetariaDishesRatio({ menuId: 123456 })

Without Diesis


const getDishes = (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
}

const countVegetarianDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.filter((dish) =>
    dish.vegetarian).length
}

const countDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.length
}

const getVegetarianDishesRatio = async ({ menuId }) => {
  const nDishes = await countDishes({ menuId })
  const nVeg = await vegetarianDishesCourses({ menuId })
  return nVeg / nDishes
}

Without Diesis (now correct)


const getDishes = (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
}

const countVegetarianDishesFromList = (dishes) =>
  dishes.filter((d) => m.vegetarian).length

const countVegetarianDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return countVegetarianDishesFromList(dishes)
}

const countDishesFromList = (dishes) => dishes.length

const countDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return countDishesFromList(dishes)
}

const getVegetarianDishesRatio = async ({ menuId }) => {
  const dishes = await getDishes(manuId)
  const nDishes = countDishesFromList(dishes)
  const nVegs = countVegetarianDishesFromList(dishes)
  return nVegs / nDishes
}

With Diesis


const getDishes = dependency(['menuId'], (menuId) => {
  // ...
  return Promise.resolve([
    { name: 'Pasta Bolognese', vegetarian: false },
    { name: 'Pasta with tomato', vegeratian: true },
    { name: 'Pasta aglio olio', vegeratian: true },
    { name: 'Pasta with meatballs', vegeratian: false }
  ])
})

const countVegetarianDishes = dependency([getDishes], (dishes) =>
  dishes.filter((dish) => dish.vegetarian).length)

const countDishes = dependency([getDishes], (dishes) => {
  return dishes.length
})

const getMainCourseRatio = dependency([countVegetarianDishes, countDishes],
(nVegs, nDishes) => {
  return nVegs / nDishes
})

Testing without Diesis


const countDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return dishes.length
}

// :-(

const countDishesFromList = (dishes) => dishes.length

const countDishes = async ({ menuId }) => {
  const dishes = await getDishes(menuId)
  return countDishesFromList(dishes)
}

countDishesFromList(['pasta1', 'pasta2', 'pasta3'])
  .then(n => {
    assert(n === 3)
  })

// :-|

Testing with Diesis


const countDishes = dependency([getDishes], (dishes) => {
  return dishes.length
})


assert(countDishes.dep.func(['pasta1', 'pasta2', 'pasta3']) === 3)

run multiple dependencies


// Don't do that!

const [vegetarianDishesRatio, vegetarianDishes] = await Promise.all([
  run(getVegetarianDishesRatio, { menuId: 123456 }),
  run(countVegetarianDishes, { menuId: 123456 })
])

// Do that instead!
const { run } = require('diesis')

const [vegetarianDishesRatio, vegetarianDishes] = await run([
  getVegetarianDishesRatio,
  countVegetarianDishes
], { menuId: 123456 })

Memoize dependencies


const { dependencyMemo } = require('diesis')

const database =  dependencyMemo([], () => {
  // ...
})

@sithmel

Thanks and buon appetito

diesis

By Maurizio Lupo

diesis

A dependency injection library

  • 611