Skip to content

Instantly share code, notes, and snippets.

@Restuta
Last active January 25, 2019 22:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Restuta/a51bd2cc768a27dead559297e8c91d39 to your computer and use it in GitHub Desktop.
Save Restuta/a51bd2cc768a27dead559297e8c91d39 to your computer and use it in GitHub Desktop.
Audit Log Collection Design Problem
/*
Problem
We have a function that updates objects and generates audit records of the changes.
Think about financial systems or just any app where it's useful to know who made changes to what.
Audit records needs to be saved to the DB as well as updates to objects, but ideally
we would want to execute business logic, accumulate all updates and audit records in memory
and then save it to DB all at once. Let's look at the specifics.
/* Take a look at this "foo" function, think about it as our "business logic".
* We want to abstract audit records collection from "foo" function. We don't want it to know
* anything about audit log, it should just be able to call "applyUpdatesWithAudit" anytime
* it wants. Later we would like to collect all audit logs and save to DB. */
const foo = applyUpdatesWithAudit => (obj, userName) => {
let updatedObj = applyUpdatesWithAudit(obj, { y: 1 }, userName);
updatedObj = applyUpdatesWithAudit(updatedObj, { z: 2 }, userName);
updatedObj = applyUpdatesWithAudit(updatedObj, { x: 3 }, userName);
return {
updatedObj,
};
};
// "updateWithAuditLog" is a function that updates an object and returns updated object and
// corresponding audit record. Do not change it's implementation, besides what is required to
// change aurity and types of arguments or return values.
const updateWithAuditLog = (orignalObj, updates, userName) => {
const updatedObj = {
...orignalObj,
...updates,
};
const auditRecord = `${userName} updated: "${Object.keys(updates).join(',')}".`;
return {
updatedObj,
auditRecord,
};
};
// use this function to "pretend" it saves records to DB
const saveAuditRecordsToDb = async auditRecords =>
console.log(`Saved ${auditRecords.length} records to db`);
const saveUpdatesToDb = async updates =>
console.log(`Saved ${JSON.stringify(updates)} to db`);
/* Task
* Design a pattern that allows "foo" function to be agnostic of the update function passed to it,
* it should not deal with audit logs collection or saving them to db.
*
* End result should look like this:
*/
const myFoo = createFoo(foo, updateWithAuditLog)
/*
* my foo should be a function that accepts same arguments as foo after providing update function.
* e.g.: foo(updateWithAuditLog)
* But also does exactly two DB calls, to save updates and to save all audit logs.
*/
// Example usage:
await myFoo(obj, userName)
// Feel free to change function aurity, signatures, incoming and return types.
/* Goals
*
* Solutions should be intutitive to use. By looking at the foo function's code, it should
* be somewhat obvious that audit collection is happening.
* By looking a the rest of the solution it should be obvious _how_ it is happening.
*
* Provide explanation of explored approaches and tredeoffs.
* /
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment