{"id":22062654,"url":"https://github.com/ryan-haskell/ts-functional-architecture","last_synced_at":"2025-06-12T00:33:26.846Z","repository":{"id":88811538,"uuid":"407925889","full_name":"ryan-haskell/ts-functional-architecture","owner":"ryan-haskell","description":"a functional style way to architect your typescript app","archived":false,"fork":false,"pushed_at":"2021-09-19T07:02:24.000Z","size":110,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-09T19:55:44.082Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ryan-haskell.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-09-18T17:33:26.000Z","updated_at":"2024-09-25T08:16:20.000Z","dependencies_parsed_at":"2023-06-12T18:45:44.760Z","dependency_job_id":null,"html_url":"https://github.com/ryan-haskell/ts-functional-architecture","commit_stats":null,"previous_names":["ryan-haskell/ts-functional-architecture"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ryan-haskell/ts-functional-architecture","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryan-haskell%2Fts-functional-architecture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryan-haskell%2Fts-functional-architecture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryan-haskell%2Fts-functional-architecture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryan-haskell%2Fts-functional-architecture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ryan-haskell","download_url":"https://codeload.github.com/ryan-haskell/ts-functional-architecture/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ryan-haskell%2Fts-functional-architecture/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259370100,"owners_count":22847218,"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","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":[],"created_at":"2024-11-30T18:26:04.019Z","updated_at":"2025-06-12T00:33:26.764Z","avatar_url":"https://github.com/ryan-haskell.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TypeScript Functional Architecture\n\n\nA backend architecture loosely based off of the \"Clean\" Architecture, but without using classes because inheritance is bad and the behavior of the `this` keyword is inconsistent and spooky in the TS/JS ecosystem.\n\n## Layers\n\nThis project is broken up into layers so that each piece of our codebase is:\n1. Focused on one thing\n1. Easier to test (and those tests run faster!)\n\n\n```bash\nsrc/\n  - core/...\n  - domain/...\n  - graphql/...\n  - repos/...\n  - useCases/...\n```\n\nHere's a breakdown of the layers you will find in this project, and their individual purposes:\n\n### Core\n\nThe \"core\" folder is not actually a layer. It provides some functions and types for making this architecture framework possible.\n\nLet's move onto our first _real_ layer!\n\n### Domain\n\nFiles in the __domain layer__ are simple data structures that can only be composed of built-in values, data structures, or other domain items.\n\nThis domain model __does not__ come from an ORM or any value tied into your persistence model.\n\nFor developer convenience, this is also a good place to define simple utility operations for transforming that one domain data structure into values.\n\nAn example of this might be getting the `Person.firstName`\n\n```ts\nimport { Id } from '@domain/id'\n\nexport type Person = {\n  id: Id,\n  fullname: string\n  email: string\n}\n\nexport const Person = {\n  firstName: (person: Person) =\u003e string,\n  lastName: (person: Person) =\u003e string\n}\n```\n\n### GraphQL\n\nOur API users can send us garbage over the wire. GraphQL helps with this, but we'll still need to transform data as it enters and exits our API.\n\nIt's up to the __graphql layer__ to translate the user input into _domain values_, call _use cases_ that run the operation we want, and transform the domain values returned by that use case to match our GraphQL schema.\n\n### Repo\n\nThe __repo layer__ is the only layer that creates side-effects in your application. A side-effect might be saving a person to a database, sending an audit log, or interacting with a 3rd party API.\n\nIf we were crazy extremists, `Repo.Console` would be the only way to make a `console.log`- but we aren't.\n\nEach repo defines it's interface as a `type` and two versions of itself:\n- The actual implementation that persists things to a database, sends a log, works with an API\n- The _default_* mock implementation of this repository. I've emphasized \"default\" here because (when we get to the testing section of the guide) we'll reveal ways for a test to provide alternate behavior for mocking repo behavior.\n\n### Use cases\n\nThe __use case layer__ is where our actual business logic lives. Each use case defines:\n\n- the input it expects\n- the dependencies it needs (repos, current user, etc)\n- the success value it should return\n- the problems it might run into.\n\nBecause use cases have their dependencies passed in with their input, they become incredibly efficient to test, and relatively easy to scan. It's important that use cases do not worry about:\n\n- User input / output (that's the `graphql` layer's job!)\n- Saving to databases or calling APIs (that's the `repo` layer's job!)\n\n#### __Testing__\n\nFor every use-case in the `useCases` directory, there is an associated `.test.ts` file. Each one of these files define a test suite that describes the expected output for a given input.\n\nEach test also allows you to override it's dependencies- so you can mock different edge cases easily.\n\nCheck out the example at [useCases/personCreate.test.ts](./src/useCases/personCreate.test.ts), or run it from the console with `npx jest`:\n\n```\n$ npx jest\n\n PASS  src/useCases/personCreate.test.ts\n  ✓ When email address is already taken, it returns a problem. (2 ms)\n  ✓ When all input is valid, it creates a new user.\n\nTest Suites: 1 passed, 1 total\nTests:       2 passed, 2 total\nSnapshots:   0 total\nTime:        0.613 s, estimated 1 s\nRan all test suites.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryan-haskell%2Fts-functional-architecture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fryan-haskell%2Fts-functional-architecture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fryan-haskell%2Fts-functional-architecture/lists"}