{"id":13467637,"url":"https://github.com/stemmlerjs/ddd-forum","last_synced_at":"2025-05-15T12:04:14.060Z","repository":{"id":37270215,"uuid":"212189690","full_name":"stemmlerjs/ddd-forum","owner":"stemmlerjs","description":"Hacker news-inspired forum app built with TypeScript using DDD practices from solidbook.io.","archived":false,"fork":false,"pushed_at":"2023-06-10T05:59:04.000Z","size":1632,"stargazers_count":2013,"open_issues_count":82,"forks_count":453,"subscribers_count":57,"default_branch":"master","last_synced_at":"2025-04-14T19:59:34.300Z","etag":null,"topics":["ddd","hackernews","typescript"],"latest_commit_sha":null,"homepage":"https://dddforum.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stemmlerjs.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}},"created_at":"2019-10-01T20:07:57.000Z","updated_at":"2025-04-14T14:29:34.000Z","dependencies_parsed_at":"2024-01-07T21:05:13.600Z","dependency_job_id":"81b32014-b43a-458c-b720-b0be858e58e8","html_url":"https://github.com/stemmlerjs/ddd-forum","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stemmlerjs%2Fddd-forum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stemmlerjs%2Fddd-forum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stemmlerjs%2Fddd-forum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stemmlerjs%2Fddd-forum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stemmlerjs","download_url":"https://codeload.github.com/stemmlerjs/ddd-forum/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254337612,"owners_count":22054253,"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":["ddd","hackernews","typescript"],"created_at":"2024-07-31T15:00:58.737Z","updated_at":"2025-05-15T12:04:09.047Z","avatar_url":"https://github.com/stemmlerjs.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\n\n\n\u003cp align=\"center\"\u003e\n \u003cimg src=\"https://user-images.githubusercontent.com/6892666/67032637-fc237200-f0e1-11e9-8a46-f5d655e71962.png\"/\u003e\n\u003c/p\u003e\n\u003ch1 align=\"center\"\u003eDDDForum.com\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n \u003ca href=\"https://circleci.com/gh/stemmlerjs/ddd-forum\"\u003e\u003cimg src=\"https://circleci.com/gh/circleci/circleci-docs.svg?style=svg\"\u003e\u003c/a\u003e\n \u003ca href=\"#contributors\"\u003e\u003cimg src=\"https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003e A [SOLID](https://khalilstemmler.com/articles/solid-principles/solid-typescript/) hackernews-inspired forum site built with TypeScript using the [clean architecture](https://khalilstemmler.com/articles/software-design-architecture/organizing-app-logic/) and [DDD best practices](https://khalilstemmler.com/articles/domain-driven-design-intro/).\n\n![DDDForum](https://user-images.githubusercontent.com/6892666/67032446-9931db00-f0e1-11e9-894d-7bccd240c851.png)\n\n## About \n\nDDDForum.com is the application that we build in [solidbook.io - The Software Design and Architecture Handbook](https://solidbook.io). \n\n## Running the project\n\n1. Install and start [Docker](https://docs.docker.com/compose/gettingstarted/) if you haven't already.\n2. Copy the `.env` template file. Feel free to change passwords and app secrets. \n\n```bash\ncp .env.template .env\n```\n\n3. Build and run the image to run the backend services.\n\n```bash\ndocker-compose up\n```\n\n4. Open up an additional console and then run:\n\n```bash\nnpm run setup:dev\nnpm run start:both\n```\n\nYou can visit the app by going to `http://localhost:3000`.\n\n### Demo \n\n[You can visit the site here](https://dddforum.com).\n\n\u003e `Note`: It's currently deployed on free tier Heroku, which has some undesirable side-effects like shutting off the server during periods of inactivity. So if it's down for you, refresh a couple of times. Thinking about migrating this to a serverless architecture later on.\n\n### Built with\n\n#### Backend\n\n- [Sequelize](https://github.com/sequelize/sequelize) - The ORM for Node.js\n- [Express.js](https://expressjs.com/) - Lightweight webserver\n- [Redis](https://redis.io/) - For holding onto JWT tokens and refresh tokens\n\n#### Frontend\n\n- [React.js](https://reactjs.org/)\n- [Redux](https://redux.js.org/)\n- [Sass](https://sass-lang.com/)\n\n### Architecture\n\nWe built this based on the [Clean Architecture](https://khalilstemmler.com/articles/software-design-architecture/organizing-app-logic/), [SOLID principles](https://khalilstemmler.com/articles/solid-principles/solid-typescript/), and [Domain-Driven Design](https://khalilstemmler.com/articles/domain-driven-design-intro/) best practices using TypeScript.\n\n#### Clean architecture\n\nThere's obviously a lot that went into building this from front to back.\n\nThe **Clean Architecture** is a way to reason about where different types of application logic belongs. \n\n\u003cimg width=\"1586\" alt=\"Frame 8 (1)\" src=\"https://user-images.githubusercontent.com/6892666/66703014-dc540e80-ecdb-11e9-81ac-9cc24e28f8c3.png\"\u003e\n\n\nThere's a lot more to learn about the clean architecture, but for now- just know that it's a way to really separate the concerns of everything that goes into building complex enterprise applications. You'll never see any `infrastructure`-related code alongside `domain` layer code.\n\nThe clean architecture, when combined with Domain-Driven Design, is very powerful :) \n\nIn DDD, we build applications on top of a number of subdomains.\n\n##### Subdomains\n\n\u003e A _subdomain_ is a cohesive unit of code that represents exactly one core concept and is responsible for a specific set of concerns in an application architecture. For example, every appliciation has a `users` subdomain. That's responsible for _users, identity \u0026 access management, authentication, authorization, etc_. Sometimes you don't want to build that yourself. Sometimes you can go with an off-the-shelf solution like [Auth0](https://auth0.com/). But there are subdomains in your application that you cannot simply outsource. These are the **family jewels**; the thing that's actually _novel_ about your app. This is the subdomain that no one (except you) can code. Know why? Because only _you_ have the domain knowledge to build it exactly the way that it should be built. You understand the domain. \n\nIn DDDForum, we have 2 **subdomains**: The `users` subdomain and the `forum` subdomain.\n\n\u003cimg width=\"855\" alt=\"Frame 3 (1)\" src=\"https://user-images.githubusercontent.com/6892666/66702871-4a97d180-ecda-11e9-9a80-19755dc1f0a9.png\"\u003e\n\nEach subdomain has a:\n\n- `domain` layer: where the highest-level policy, domain objects, and domain rules belong (`user`, `email`, etc)\n- `application` layer: where the use cases / features that utilize domain objects belong (`createUser`, `login`, etc)\n- `adapter` layer: where we define abstractions so that `application` layer code can interact with `infrastructure` layer concepts, without actually requiring on `infrastructure` (because that would break the [dependency rule](https://khalilstemmler.com/wiki/dependency-rule/)). Here we write things like `IUserRepo` - repository adapter, `IJWTTokenService` - an abstraction of a cache (redis) that manages tokens, etc.\n- `infrastructure` layer: where we create [concrete](https://khalilstemmler.com/wiki/concrete-class/) implementations of the abstractions from the `adapter` layer so that they can be spun up at runtime thanks to the power of polymorhpism :) (more on this later).\n\n\u003e If you haven't already, I recommend you read [this article](https://khalilstemmler.com/articles/enterprise-typescript-nodejs/application-layer-use-cases/) on use cases and subdomains.\n\nLet's identify some of the actual concepts that exist in each subdomain.\n\n### `users` subdomain\n\nIn the` users` subdomain, we're only concerned with concepts that are related to authentication, roles, etc. Here are a few examples of classes and concepts that exist at each layer.\n\n- `domain` layer: `user` ([aggregate root](https://khalilstemmler.com/articles/typescript-domain-driven-design/aggregate-design-persistence/)), `userEmail` ([value object](https://khalilstemmler.com/articles/typescript-value-object/)), `userCreated` ([domain event](https://khalilstemmler.com/articles/typescript-domain-driven-design/chain-business-logic-domain-events/)).\n- `application` layer: `createUserUseCase` ([use case](https://khalilstemmler.com/articles/enterprise-typescript-nodejs/application-layer-use-cases/)), `getUserByUserName` (use case).\n- `adapter` layer: `IUserRepo` ([respository](https://khalilstemmler.com/articles/typescript-domain-driven-design/repository-dto-mapper/) interface adapter)\n- `infrastructure` layer: `SequelizeUserRepo` (a concrete implementation of the IUserRepo), `UserDTO` ([data transmission objects](https://khalilstemmler.com/articles/typescript-domain-driven-design/repository-dto-mapper/)).\n\n### `forum` subdomain\n\nIn the `forum` subdomain, we're only concerned with concepts that have to do with building a forum. You won't see any domain concepts from the `user` in `forum`. In the `forum` subdomain, the concept most equivalent to a `user`, is a `member`.\n\nHere are a few examples of concepts from the `forum` subdomain.\n\n- `domain` layer: `member`, `comment`, `post`, `postVote`, `commentVote`, `commentVotesChanged`\n- `application` layer: `replyToComment`, `getMemberByUserName`,  `upvotePost`, `downvotePost`\n- `adapter` layer: `ICommentRepo`, `IPostRepo`, `IMemberRepo`\n- `infrastructure` layer: `SequelizeCommentRepo`, `SequelizePostRepo`, `SequelizeMemberRepo`\n\n## Project visualization\n\nHere's a large-scale visualization of the repo. As I put more time into the front-end, it may change a little bit.\n\n![Visualization of this repo](./diagram.svg)\n\n## Contributing\n\nDDDForum is an open source project, and contributions of any kind are welcome! Open issues, bugs, and enhancements are all listed on the issues tab and labeled accordingly. Feel free to open bug tickets and make feature requests. Easy bugs and features will be tagged with the good first issue label.\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://linkedin.com/in/anthonydenneulin\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1647464?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eAnthony Denneulin\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/stemmlerjs/ddd-forum/commits?author=denneulin\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"http://khalilstemmler.com/\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/6892666?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKhalil Stemmler\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/stemmlerjs/ddd-forum/commits?author=stemmlerjs\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/stemmlerjs/ddd-forum/issues?q=author%3Astemmlerjs\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"https://github.com/stemmlerjs/ddd-forum/commits?author=stemmlerjs\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#infra-stemmlerjs\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"#ideas-stemmlerjs\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://medium.com/@faisol.chehumar\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/30441151?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eFaisol Chehumar\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/stemmlerjs/ddd-forum/commits?author=faisol-chehumar\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://trungtran.io/\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/22028884?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eTrung Tran\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"#infra-ChunML\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n\n## License\n\nThis project is licensed under the ISC License - see the [LICENSE.md](https://github.com/stemmlerjs/ddd-forum/blob/master/LICENCE.md) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstemmlerjs%2Fddd-forum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstemmlerjs%2Fddd-forum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstemmlerjs%2Fddd-forum/lists"}