ddd-forum
Hacker news-inspired forum app built with TypeScript using DDD practices from solidbook.io.
Top Related Projects
Learn Domain-Driven Design, software architecture, design patterns, best practices. Code examples included
Full Modular Monolith application with Domain-Driven Design approach.
These are the sample Bounded Contexts from the book "Implementing Domain-Driven Design" by Vaughn Vernon: http://vaughnvernon.co/?page_id=168
Clean Architecture Solution Template: A starting point for Clean Architecture with ASP.NET Core
Quick Overview
DDD Forum is a full-stack TypeScript application that demonstrates Domain-Driven Design (DDD) principles in a real-world scenario. It's a forum application built using Clean Architecture, SOLID principles, and various design patterns, serving as an educational resource for developers interested in learning and implementing DDD concepts.
Pros
- Comprehensive implementation of DDD concepts in a practical, full-stack application
- Well-structured codebase with clear separation of concerns and modular architecture
- Extensive documentation and explanations of design decisions and patterns used
- Utilizes modern technologies and best practices in TypeScript development
Cons
- Complex architecture may have a steep learning curve for beginners
- Might be over-engineered for simple applications or small-scale projects
- Limited community contributions and updates
- Lack of extensive test coverage in some areas of the project
Code Examples
- Creating a new user:
const userResult = await UserMap.toDomain({
username,
email,
password
});
const user: User = userResult.getValue();
await this.userRepo.save(user);
This code snippet demonstrates the creation of a new user using the UserMap to convert input data to a domain object, then saving it to the repository.
- Implementing a domain event:
export class MemberCreated implements IDomainEvent {
public dateTimeOccurred: Date;
public member: Member;
constructor (member: Member) {
this.dateTimeOccurred = new Date();
this.member = member;
}
getAggregateId (): UniqueEntityID {
return this.member.id;
}
}
This example shows the implementation of a domain event for when a new member is created, following DDD principles.
- Using the Result class for error handling:
public static create (props: UserProps): Result<User> {
const guardResult = Guard.againstNullOrUndefinedBulk([
{ argument: props.username, argumentName: 'username' },
{ argument: props.email, argumentName: 'email' }
]);
if (!guardResult.succeeded) {
return Result.fail<User>(guardResult.message);
}
return Result.ok<User>(new User(props));
}
This code demonstrates the use of the Result class for handling errors and validations when creating a User entity.
Getting Started
To get started with the DDD Forum project:
-
Clone the repository:
git clone https://github.com/stemmlerjs/ddd-forum.git
-
Install dependencies:
cd ddd-forum npm install
-
Set up the database (instructions in the project's README)
-
Start the development server:
npm run start:dev
-
Access the application at
http://localhost:3000
For more detailed instructions and explanations, refer to the project's documentation and README file.
Competitor Comparisons
Learn Domain-Driven Design, software architecture, design patterns, best practices. Code examples included
Pros of domain-driven-hexagon
- More comprehensive documentation and explanations of DDD concepts
- Includes examples of various architectural patterns (CQRS, Event Sourcing)
- Provides a more flexible and modular structure for larger applications
Cons of domain-driven-hexagon
- May be overwhelming for beginners due to its extensive coverage
- Less focused on a specific application example compared to ddd-forum
- Requires more setup and configuration for a complete project
Code Comparison
ddd-forum example:
export class User extends AggregateRoot<UserProps> {
private constructor(props: UserProps) {
super(props);
}
public static create(props: UserProps): Result<User> {
// User creation logic
}
}
domain-driven-hexagon example:
export class User extends AggregateRoot {
private constructor(props: UserProps) {
super();
this.props = props;
}
public static create(props: UserProps): Result<User> {
// User creation logic with more validation
}
}
Both repositories demonstrate DDD principles, but domain-driven-hexagon offers a more comprehensive approach with additional architectural patterns. ddd-forum provides a more focused, real-world example of a forum application, which may be easier for beginners to grasp. The code structures are similar, with domain-driven-hexagon potentially offering more flexibility for complex domain models.
Full Modular Monolith application with Domain-Driven Design approach.
Pros of modular-monolith-with-ddd
- Implements a modular monolith architecture, which can be easier to maintain and deploy compared to microservices
- Provides a more comprehensive example of DDD implementation, including bounded contexts and CQRS
- Includes detailed documentation and architectural decision records (ADRs)
Cons of modular-monolith-with-ddd
- Uses C# instead of TypeScript, which may be less accessible for web developers
- Focuses on a more complex domain (meetings and administration) compared to a simpler forum application
- Lacks a front-end implementation, making it less suitable for full-stack learning
Code Comparison
modular-monolith-with-ddd (C#):
public class MeetingGroup : Entity, IAggregateRoot
{
public MeetingGroupId Id { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public MeetingGroupLocation Location { get; private set; }
public MemberId CreatorId { get; private set; }
// ...
}
ddd-forum (TypeScript):
export class Post extends AggregateRoot<PostProps> {
private constructor (props: PostProps, id?: UniqueEntityID) {
super(props, id);
}
public static create (props: PostProps, id?: UniqueEntityID): Result<Post> {
// ...
}
}
Both repositories demonstrate DDD principles, but modular-monolith-with-ddd provides a more comprehensive example with additional architectural patterns. ddd-forum offers a simpler, web-focused implementation using TypeScript, making it more accessible for front-end developers interested in DDD.
These are the sample Bounded Contexts from the book "Implementing Domain-Driven Design" by Vaughn Vernon: http://vaughnvernon.co/?page_id=168
Pros of IDDD_Samples
- Covers a wider range of DDD concepts and patterns
- Provides examples in multiple programming languages (Java, .NET, Scala)
- Includes more complex domain models and scenarios
Cons of IDDD_Samples
- Less focus on modern web development practices
- Older codebase with potentially outdated dependencies
- Lacks a complete, end-to-end application example
Code Comparison
IDDD_Samples (Java):
public class BacklogItem extends ConcurrencySafeEntity {
private BacklogItemId backlogItemId;
private String summary;
private String category;
private BacklogItemStatus status;
// ...
}
ddd-forum (TypeScript):
export class Post extends AggregateRoot<PostProps> {
private constructor(props: PostProps, id?: UniqueEntityID) {
super(props, id);
}
public static create(props: PostProps, id?: UniqueEntityID): Result<Post> {
// ...
}
}
IDDD_Samples focuses on traditional OOP approaches, while ddd-forum employs modern TypeScript features and functional programming concepts. The ddd-forum example showcases a more explicit creation process and uses a Result type for error handling.
Both repositories offer valuable insights into DDD implementation, with IDDD_Samples providing a broader range of examples across multiple languages, and ddd-forum demonstrating a more focused, modern web development approach using TypeScript and Node.js.
Clean Architecture Solution Template: A starting point for Clean Architecture with ASP.NET Core
Pros of CleanArchitecture
- More comprehensive implementation of Clean Architecture principles
- Better separation of concerns with distinct layers (Core, Infrastructure, Web)
- Includes example unit and integration tests
Cons of CleanArchitecture
- Less focus on Domain-Driven Design (DDD) concepts
- Simpler domain model, not as rich in business logic
- Fewer real-world use cases demonstrated
Code Comparison
CleanArchitecture:
public class ToDoItemService : IToDoItemService
{
private readonly IRepository<ToDoItem> _repository;
public ToDoItemService(IRepository<ToDoItem> repository)
{
_repository = repository;
}
}
ddd-forum:
export class CommentService {
private commentRepo: ICommentRepo;
private postRepo: IPostRepo;
constructor (commentRepo: ICommentRepo, postRepo: IPostRepo) {
this.commentRepo = commentRepo;
this.postRepo = postRepo;
}
}
Both examples show dependency injection and repository usage, but ddd-forum demonstrates a more complex domain model with multiple repositories.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
DDDForum.com
A SOLID hackernews-inspired forum site built with TypeScript using the clean architecture and DDD best practices.
About
DDDForum.com is the application that we build in solidbook.io - The Software Design and Architecture Handbook.
Running the project
- Install and start Docker if you haven't already.
- Copy the
.env
template file. Feel free to change passwords and app secrets.
cp .env.template .env
- Build and run the image to run the backend services.
docker-compose up
- Open up an additional console and then run:
npm run setup:dev
npm run start:both
You can visit the app by going to http://localhost:3000
.
Demo
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.
Built with
Backend
- Sequelize - The ORM for Node.js
- Express.js - Lightweight webserver
- Redis - For holding onto JWT tokens and refresh tokens
Frontend
Architecture
We built this based on the Clean Architecture, SOLID principles, and Domain-Driven Design best practices using TypeScript.
Clean architecture
There's obviously a lot that went into building this from front to back.
The Clean Architecture is a way to reason about where different types of application logic belongs.
There'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.
The clean architecture, when combined with Domain-Driven Design, is very powerful :)
In DDD, we build applications on top of a number of subdomains.
Subdomains
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 & 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. 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.
In DDDForum, we have 2 subdomains: The users
subdomain and the forum
subdomain.
Each subdomain has a:
domain
layer: where the highest-level policy, domain objects, and domain rules belong (user
,email
, etc)application
layer: where the use cases / features that utilize domain objects belong (createUser
,login
, etc)adapter
layer: where we define abstractions so thatapplication
layer code can interact withinfrastructure
layer concepts, without actually requiring oninfrastructure
(because that would break the dependency rule). Here we write things likeIUserRepo
- repository adapter,IJWTTokenService
- an abstraction of a cache (redis) that manages tokens, etc.infrastructure
layer: where we create concrete implementations of the abstractions from theadapter
layer so that they can be spun up at runtime thanks to the power of polymorhpism :) (more on this later).
If you haven't already, I recommend you read this article on use cases and subdomains.
Let's identify some of the actual concepts that exist in each subdomain.
users
subdomain
In 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.
domain
layer:user
(aggregate root),userEmail
(value object),userCreated
(domain event).application
layer:createUserUseCase
(use case),getUserByUserName
(use case).adapter
layer:IUserRepo
(respository interface adapter)infrastructure
layer:SequelizeUserRepo
(a concrete implementation of the IUserRepo),UserDTO
(data transmission objects).
forum
subdomain
In 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
.
Here are a few examples of concepts from the forum
subdomain.
domain
layer:member
,comment
,post
,postVote
,commentVote
,commentVotesChanged
application
layer:replyToComment
,getMemberByUserName
,upvotePost
,downvotePost
adapter
layer:ICommentRepo
,IPostRepo
,IMemberRepo
infrastructure
layer:SequelizeCommentRepo
,SequelizePostRepo
,SequelizeMemberRepo
Project visualization
Here's a large-scale visualization of the repo. As I put more time into the front-end, it may change a little bit.
Contributing
DDDForum 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.
Contributors â¨
Thanks goes to these wonderful people (emoji key):
Anthony Denneulin ð» |
Khalil Stemmler ð» ð ð ð ð¤ |
Faisol Chehumar ð» |
Trung Tran ð |
This project follows the all-contributors specification. Contributions of any kind welcome!
License
This project is licensed under the ISC License - see the LICENSE.md file for details
Top Related Projects
Learn Domain-Driven Design, software architecture, design patterns, best practices. Code examples included
Full Modular Monolith application with Domain-Driven Design approach.
These are the sample Bounded Contexts from the book "Implementing Domain-Driven Design" by Vaughn Vernon: http://vaughnvernon.co/?page_id=168
Clean Architecture Solution Template: A starting point for Clean Architecture with ASP.NET Core
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot