Convert Figma logo to code with AI

stemmlerjs logoddd-forum

Hacker news-inspired forum app built with TypeScript using DDD practices from solidbook.io.

1,937
395
1,937
81

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

  1. 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.

  1. 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.

  1. 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:

  1. Clone the repository:

    git clone https://github.com/stemmlerjs/ddd-forum.git
    
  2. Install dependencies:

    cd ddd-forum
    npm install
    
  3. Set up the database (instructions in the project's README)

  4. Start the development server:

    npm run start:dev
    
  5. 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 Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

DDDForum.com

A SOLID hackernews-inspired forum site built with TypeScript using the clean architecture and DDD best practices.

DDDForum

About

DDDForum.com is the application that we build in solidbook.io - The Software Design and Architecture Handbook.

Running the project

  1. Install and start Docker if you haven't already.
  2. Copy the .env template file. Feel free to change passwords and app secrets.
cp .env.template .env
  1. Build and run the image to run the backend services.
docker-compose up
  1. 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

You can visit the site here.

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.

Frame 8 (1)

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.

Frame 3 (1)

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 that application layer code can interact with infrastructure layer concepts, without actually requiring on infrastructure (because that would break the dependency rule). Here we write things like IUserRepo - repository adapter, IJWTTokenService - an abstraction of a cache (redis) that manages tokens, etc.
  • infrastructure layer: where we create concrete 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).

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.

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.

Visualization of this repo

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