{"id":22474693,"url":"https://github.com/herbsjs/buchu","last_synced_at":"2025-08-02T11:32:12.149Z","repository":{"id":40265679,"uuid":"199334043","full_name":"herbsjs/buchu","owner":"herbsjs","description":"Use Cases - Uniform, auditable and secure use case library","archived":false,"fork":false,"pushed_at":"2023-11-22T14:40:57.000Z","size":1865,"stargazers_count":23,"open_issues_count":8,"forks_count":22,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-07-06T07:07:27.256Z","etag":null,"topics":["clean-architecture","cleanarchitecture","ddd","domain-driven-design","hacktoberfest","herbsjs","javascript","usecase"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/herbsjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-28T20:29:36.000Z","updated_at":"2025-05-12T18:40:54.000Z","dependencies_parsed_at":"2023-11-22T16:08:26.928Z","dependency_job_id":"3fb912bd-84d6-4781-8f53-56bc9a1fdf21","html_url":"https://github.com/herbsjs/buchu","commit_stats":{"total_commits":224,"total_committers":26,"mean_commits":8.615384615384615,"dds":0.8571428571428572,"last_synced_commit":"a3381b6c0c90f5a6bcbb5fb14a562031f425dcce"},"previous_names":["dalssoft/grounds"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/herbsjs/buchu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fbuchu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fbuchu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fbuchu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fbuchu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/herbsjs","download_url":"https://codeload.github.com/herbsjs/buchu/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/herbsjs%2Fbuchu/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268378965,"owners_count":24240907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","cleanarchitecture","ddd","domain-driven-design","hacktoberfest","herbsjs","javascript","usecase"],"created_at":"2024-12-06T13:09:56.012Z","updated_at":"2025-08-02T11:32:11.773Z","avatar_url":"https://github.com/herbsjs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/herbsjs/buchu/master/docs/logo.png\" height=\"220\"\u003e\u003c/p\u003e\n\n![CI build](https://github.com/herbsjs/buchu/workflows/Node.js%20CI/badge.svg) [![codecov](https://codecov.io/gh/herbsjs/buchu/branch/master/graph/badge.svg)](https://codecov.io/gh/herbsjs/buchu)\n\n# buchu\n\nUniform, auditable and secure use case javascript library. Influenced by Clean Architecture and Trailblazer\n\n\n## index\n\n- [Installing](#installing)\n- [Using](#using)\n- [Motivations](#motivations)\n    - [Maintainability](#maintainability)\n    - [Metadata for system intent](#metadata-for-system-intent)\n    - [Auditable and Secure](#auditable-and-ecure)\n- [Audit](#audit)\n- [Request Validation](#request-validation)\n- [Authorization](#authorization)\n- [Use Case](#use-case)\n    - [What is it?](#what-is-it?)\n    - [Best-pratices](#best-pratices)\n- [Errors](#errors)\n- [Contribute](#contribute)\n- [The Herb](#the-herb)\n- [License](#license)\n\n\n### Installing\n``` $ npm install @herbsjs/buchu ```\n\n### Using\n\nCheck the complete examples [here](https://github.com/herbsjs/buchu/tree/master/examples) or for a complete solution using herbsJS [here](https://github.com/herbsjs/todolist-on-herbs).\n\nusecases/addOrUpdateItem.js:\n\n```javascript\nconst { entity, field } = require('@herbsjs/gotu')\nconst Item = entity('Item', {\n  id: field(Number),\n  description: field(String),\n  isDone: field(Boolean),\n  position: field(Number)\n})\n\nconst { Ok, Err, usecase, step, ifElse } = require('@herbsjs/buchu')\nconst dependency = {\n    ItemRepository: require('../repositories/ItemRepository').ItemRepository,\n    ...\n}\n\nconst addOrUpdateItem = (injection) =\u003e\n\n    usecase('Add or Update an Item on a to-do List', {\n\n        // Input/Request type validation\n        request: { listId: Number, item: Item },\n\n        // Output/Response type\n        response: { item: Item },\n\n        // Authorization Audit\n        authorize: async (user) =\u003e user.isAdmin ? Ok() : Err(),\n\n        // Dependency Injection control\n        setup: (ctx) =\u003e ctx.di = Object.assign({}, dependency, injection),\n\n        // Step audit and description\n        'Check if the Item is valid': step((ctx) =\u003e {\n            ...\n            return item.validate() // Ok or Error\n        }),\n\n        'Check if the List exists': step(async (ctx) =\u003e {\n            ...\n            return Ok()\n        }),\n\n        // Conditional step\n        'Add or Update the Item': ifElse({\n\n            'If the Item exists': step(async (ctx) =\u003e {\n                ...\n                return Ok(newItem)\n            }),\n\n            'Then: Add a new Item to the List': step(async (ctx) =\u003e {\n                ...\n                return ctx.ret = await itemRepo.save(item) // Ok or Error\n            }),\n\n            'Else: Update Item on the List': step(async (ctx) =\u003e {\n                ...\n                return ctx.ret = await itemRepo.save(item) // Ok or Error\n            })\n        })\n    })\n```\nto another resources like, `ctx.stop()` used to stop a use case next steps execution, look [here](https://github.com/herbsjs/buchu/tree/master/examples)    \n\ncontroler/addOrUpdateItem.js:\n\n```javascript\napp.put('/items/:item', function (req, res) {\n    const request = req.params\n    const user = { name: 'John', id: '923b8b9a', isAdmin: true } // from session\n\n    const uc = addOrUpdateItem()\n    await uc.authorize(user)\n    const ret = await uc.run(request)\n    res.send(ret)\n})\n```\n`uc.doc()`:\n\n```javascript\n{\n  type: 'use case',\n  description: 'Add or Update an Item on a to-do List',\n  request: { listId: Number, item: Item },\n  response: String,\n  steps: [\n    { type: 'step', description: 'Check if the Item is valid', steps: null },\n    { type: 'step', description: 'Check if the List exists', steps: null },\n    {\n        type: 'if else',\n        if: { type: 'step', description: 'If the Item exists', steps: null },\n        then: { type: 'step', description: 'Then: Add a new Item to the List', steps: null },\n        else: { type: 'step', description: 'Else: Update Item on the List', steps: null }\n    }\n  ]\n}\n```\n### Motivations\n\n#### Maintainability\n\n\"Programs must be written for people to read, and only incidentally for machines to execute\" - Harold Abelson, Structure and Interpretation of Computer Programs\n\nUnderstanding what a software is doing from a business perspective is a must in order to be able to change it quickly and in a sustainable way.\n\n#### Metadata for system intent\n\nIt should be easy to retrieve a system's metadata for all its use cases and steps. This info could be used to leverage innovative interfaces (ex: dynamic admin pages, use case documentations, etc), helping narrow the gap between developers, testers and product managers.\n\n#### Auditable and Secure\n\nIt should be easy to have enterprise grade features even for simple applications. Authorization and auditing, for instance, should be available out of the box. Not using should be opt-in.\n\n### Audit\n\nIt is possible to retrieve the audit trail of an use case after its execution\n\n`uc.auditTrail`:\n\n```javascript\n{\n    type: 'use case',\n    description: 'Add or Update an Item on a to-do List',\n    transactionId: '9985fb70-f56d-466a-b466-e200d1d4848c',\n    elapsedTime: 1981800n, // in nanosecods\n    user: { name: 'John', id: '923b8b9a', isAdmin: true },\n    authorized: true,\n    return: {\n        Ok: { item: { id: 100, name: 'Do not forget this', position: 9 } }\n    },\n    steps: [\n        { type: 'step', description: 'Check if the Item is valid', elapsedTime: 208201n , return: {} },\n        { type: 'step', description: 'Check if the List exists', elapsedTime: 114300n , return: {}  },\n        {\n            type: 'if else',\n            description: 'Add or Update the Item',\n            returnIf: { Ok: true },\n            returnThen: {}\n        }\n    ]\n}\n```\nYou can use like this, using a config inside use case to remove some output fields: \n\n```javascript\n\n  const givenTheSimplestUseCaseWithAuditTrail = () =\u003e {\n            const uc = usecase('A use case', {\n                auditTrail: {\n                        return: false, \n                        user: false,\n                },\n        \n                'A step': step(() =\u003e { return Ok() }),\n            })\n            return uc\n        }\n\n```\n\nIn this example, the return of the audit trail you be: \n\n```\n{\n    configuration:{output: {return: false, user: false}}\n    description:'A use case'\n    elapsedTime:362700n\n    request:null\n    steps: [ {type: 'step', description: 'A step', return: {Ok: ''}, elapsedTime: 76100n}]\n    transactionId:'cfd88c2b-1d34-4c81-a07c-ac4ea5420d04'\n    type:'use case'\n}\n\n```\n\nYou can use those configuration to remove fields:\n\n```javascript\n\nconst auditTrail = {\n        steps: false\n        request : false\n        return : false\n        user : false\n        elapsedTime : false\n  }\n\n```\n\n\nTIP: If you need to audit the exceptions thrown by the use case use `process.env.HERBS_EXCEPTION = \"audit\"`. This will swallow the exceptions and return a Err on the step. Recommended for production environments.\n\n\n### Request Validation\n\nA request can be validated against the field's type.\n\n```javascript\nconst addOrUpdateItem = (injection) =\u003e\n\n    usecase('Add or Update an Item on a to-do List', {\n\n        // Input/Request type validation\n        request: { listId: Number, item: Object },\n\n    ...\n```\n#### Request types\n\nA field in a request can be basic types from Javascript or entities created from gotu herbs lib:\n\n`Number`: double-precision 64-bit binary format IEEE 754 value\n\n`String`: a UTF‐16 character sequence\n\n`Boolean`: true or false\n\n`Date`: represents a single moment in time in a platform-independent format.\n\n`Object`: the Object class represents one of JavaScript's data types.\n\n`Array`: the Array class is a object that is used in the construction of arrays.\n\n`Entity`: entity object represents an gotu base entity.\n\n### Authorization\n\nA usecase must declare the authorization function, which receives a user parameter, defined by some middleware in the infrastructure layer, this received parameter will be included within the ctx, and can be accessed from the steps with `ctx.user`.\n\n```javascript\nauthorize: (user) =\u003e user.isAdmin ? Ok() : Err(),\n```\n\n### Use Case\n\n#### What is it?\n\nA Use Case reflects a single action exposed by the Domain to the end user. Ex: _Reopen Ticket_, _Reply Message_, _Add User_\n\nInternaly a Use Case control the interaction between Entities, Repositories (infrastructure) and other Domain components.\n\nIt should: \n\n- Be modeled around business\n- Be reusable\n- Be testable / Have clear acceptance criteria\n- Help identify right architecture\n- Ubiquitous language\n\n\"Use cases orchestrate the flow of data to and from the entities, and direct those entities to use their Critical Business Rules to achieve the goals of the use case.\" - Clean Architecture book\n\n#### Best pratices\n\n- Keep it simple by telling stories\n- Understand the big picture\n- Focus on value\n- Build the use case in steps\n\nArchitecture:\n\n- Implement business __flows__ using Use Cases.\n\n  Ex: _Reply Message_ use case interacts with `Message`, `Ticket`, `User` and others entities in order to reply a message for a user\n- Split the __flows__ in smaller steps\n- Avoid implement __validations__ using Use Cases. Prefer Entities for validations\n- Access Use Cases from outside the Domain\n\n  Use cases are the \"entry points\" for the Domain layer, so it is the only accessible layer from outside the Domain.\n- Don't access any other sublayer which belongs to Domain layer (Entities, etc) apart Use Case from outside Domain\n- Name the Use Case folder, file and its steps  as an action (verb).\n\n  Ex: `OpenTicket.js`, `ReplyMessage.js`, `AddUser.js`\n\n  Use Cases are implemented as [command patterns](https://sourcemaking.com/design_patterns/command).\n\nReferences:\n\n- Clean Architecture book [link](https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164)\n- Use Case 2.0 [link](https://www.ivarjacobson.com/sites/default/files/field_iji_file/article/use-case_2.0_final_rev3.pdf)\n- Use Case diagram [link](http://www.agilemodeling.com/artifacts/useCaseDiagram.htm)\n- Service Layer [link](https://martinfowler.com/eaaCatalog/serviceLayer.html)\n\n### Errors\n\nAs you noted into [example](#using) session you can return an Err object, this class can be an generic Err as can be a structured Err too\n\n``` js \nconst options = { message: 'message', payload: { entity: 'user' }, cause: Err(\"my message\") || new Error() }\nErr.notFound(options),\nErr.alreadyExists(options),\nErr.invalidEntity(options),\nErr.invalidArguments({ ...options, args: { name: 'cant be empty' }}),\nErr.permissionDenied(options),\nErr.unknown(options),\nErr.buildCustomErr(options),\n```\nor you can create your own structured Err\n\n``` js\nErr._buildCustomErr('ERROR_CODE', message, payload, cause)\n```\n\n### To Do\n\n- [X] Base - Run a use case \n- [X] Use Case Error - Ok or Error results for a use case (Rust inspired)\n- [X] Steps - Enable multiple steps for a use case\n- [X] Nested Steps - Enable multiple steps for a parent step\n- [ ] Nested Steps - multiple files - Enable multiple steps in diferent files for a parent step\n- [ ] Use usecase as a step\n- [X] Doc Step - Get description and structure from use case and its steps\n- [X] Request - Be able to describe and validate the use case request object\n- [X] Response - Be able to describe and validate the use case response object\n- [X] Dependency Injection (removed)\n- [X] `ctx` var - Share context between Steps\n- [X] Conditional Steps - Enable a If/Else constructor for steps\n- [X] Authorization - Be able to authorize the execution of a use case and its steps\n- [X] Audit - Be able to track use case runtime information\n- [X] Audit - Timing - Be able to track use case and its steps execution time\n- [ ] Async / MQ - Multiple Rounds - Be able to partially execute a use case and continue (when a MQ is necessary, for instance)\n- [X] transaction ID - A ID to track execution between steps\n- [ ] session ID - A ID to track execution between use cases\n- [X] Deal with exceptions\n- [ ] Deal with no default results (Ok/Err)\n- [X] Deal with async / await steps\n- [X] Use case examples\n\n\n### Contribute\n\nCome with us to make an awesome *Buchu*.\n\nNow, if you do not have technical knowledge and also have intend to help us, do not feel shy, [click here](https://github.com/herbsjs/buchu/issues) to open an issue and collaborate their ideas, the contribution may be a criticism or a compliment (why not?)\n\nIf you would like to help contribute to this repository, please see [CONTRIBUTING](https://github.com/herbsjs/buchu/blob/master/.github/CONTRIBUTING.md)\n\n### The Herb\n\nBuchu is most often used as a stimulating tonic and a diuretic. It is now commonly used to treat urinary tract infections. In the past, this herb has also been used to treat arthritis, kidney stones and gout. It can also be used externally for bruises and sprains.\n\nhttps://www.herbslist.net/\n\nhttps://en.wikipedia.org/wiki/Agathosma_betulina\n\n### License \n\n**Buchu** is released under the\n[MIT license](https://github.com/herbsjs/buchu/blob/development/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Fbuchu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fherbsjs%2Fbuchu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fherbsjs%2Fbuchu/lists"}