ts-node-app
v0.3.0
Published
CLI para geração de componentes em arquiteturas DDD, hexagonal ou Clean Architecture ...
Downloads
212
Maintainers
Readme
Architect CLI
The ts-node-app
project is a CLI for generating components in DDD, Hexagonal, Clean Architecture, and MVC architectures. Below are the available features and how to use them.
Getting Started
Before using any other commands, the first step is to initialize your TypeScript project. This sets up all necessary dependencies and configurations.
npx ts-node-app init typescript
This command will prepare your project with the required setup for a TypeScript backend application. The structure of the setup will be saved in an
architecture.json
file. You can modify this file before running thegenerate
command to adjust the structure to better suit your needs.
Usage: ts-node-app [options] [command]
Options:
-h, --help display help for command
Commands:
pull [options] <path> Generate 'architecture.json' file by path argument and save it in the root of the project
you can ignore some files or directories by comma separated and regex patterns ex: package?,node_modules
push [options] [type] Push structure to the project
generate [options] Apply structure from file architecture.json
print [options] Print current structure file architecture.json
init [options] [type] Setup all dependencies for backend TypeScript project
help [command] display help for command
Index
- Installation
- Available Commands
- Usage Examples
- Complete Project Setup
- Architecture Concepts
- Conclusion
Installation
You can install the CLI globally or as a development dependency in your project.
Running with NPX
You can run the CLI directly with npx
without the need for installation.
npx ts-node-app <command>
Running with Alias
If you install the package globally or as a development dependency, you can use the alias tsna
to run the commands.
Global Installation
npm install -g ts-node-app
Development Dependency
npm install --save-dev ts-node-app
Once installed, you can use the alias:
tsna <command>
Available Commands
init
Sets up all necessary dependencies for a backend project in TypeScript.
npx ts-node-app init <type>
type
: Project type (default:typescript
).-v, --verbose
: Prints more information during execution.
pull
Generates an architecture.json
file from the current project structure.
npx ts-node-app pull <path>
path
: Path to the project's root directory (e.g.,./src
or./
).-v, --verbose
: Prints more information during execution.-i, --ignore <any>
: Ignores specific files or directories (e.g.,node_modules,coverage,dist
).
push
Applies a project structure based on one of the supported patterns.
npx ts-node-app push <type>
type
: Architecture type (hexagonal, clean, mvc, serverless).-v, --verbose
: Prints more information during execution.
generate
Applies the structure defined in the architecture.json
file to the project.
npx ts-node-app generate
-v, --verbose
: Prints more information during execution.
print
Prints the current structure defined in the architecture.json
file.
npx ts-node-app print
-v, --verbose
: Prints more information during execution.
Usage Examples
Set Up TypeScript Project
npx ts-node-app init typescript
Generate Structure File
npx ts-node-app pull ./src
Apply Hexagonal Structure
npx ts-node-app push hexagonal
Generate Structure from File
npx ts-node-app generate
Print Current Structure
npx ts-node-app print
Complete Project Setup
When running the init
command, the project will be set up with the following scripts and dependencies:
Scripts
dev
:dotenv -e .env.development tsx watch src/main.ts
start
:node dist/main.js
build
:tsup src/main.ts
test
:echo "Error: no test specified" && exit 1
test:watch
:dotenv -e .env.test jest -- --watchAll --no-coverage
docker:dev
:npm run docker:build:dev && npm run docker:run:dev
docker:prod
:npm run docker:build:prod && npm run docker:run:prod
docker:build:dev
:docker build --target development -t application-name:dev .
docker:build:prod
:docker build --target production -t application-name:prod .
docker:run:dev
:docker run -p 3000:3000 -d application-name:dev
docker:run:prod
:docker run -d application-name:prod
docker:db:up
:docker-compose -f 'docker-compose.yml' up -d --build 'postgis'
docker:db:down
:docker-compose -f 'docker-compose.yml' down
Development Dependencies
@eslint/js
:^9.22.0
@types/jest
:^29.5.14
@types/supertest
:^6.0.2
dotenv-cli
:^8.0.0
eslint
:^9.22.0
globals
:^16.0.0
jest
:^29.7.0
supertest
:^7.0.0
ts-jest
:^29.2.6
ts-node-app
:^0.1.0
tsconfig-paths
:^4.2.0
tsup
:^8.4.0
tsx
:^4.19.3
typescript
:^5.8.2
typescript-eslint
:^8.26.1
Architecture Concepts
Clean Architecture
Clean Architecture is a software design philosophy that emphasizes the separation of concerns, making the codebase more maintainable, testable, and scalable. It aims to create a clear distinction between the business logic and the infrastructure of an application.
In Clean Architecture, the core business logic is isolated from external dependencies such as databases, frameworks, and user interfaces. This is achieved by organizing the code into layers, each with a specific responsibility.
The main layers are:
- Entities: Represent the core business objects and rules.
- Use Cases: Contain the application-specific business rules and orchestrate the flow of data between entities and external systems.
- Interface Adapters: Convert data from the format most convenient for the use cases and entities to the format most convenient for external systems.
- Frameworks and Drivers: Include external systems such as databases, web frameworks, and user interfaces.
Domain-Driven Design (DDD)
Domain-Driven Design (DDD) is a software development approach that focuses on modeling the domain of the application based on the real-world business context. It emphasizes collaboration between technical and domain experts to create a shared understanding of the domain and its complexities.
DDD introduces several key concepts:
- Entities: Objects that have a distinct identity and lifecycle.
- Value Objects: Immutable objects that describe aspects of the domain with no distinct identity.
- Aggregates: Clusters of entities and value objects that are treated as a single unit for data changes.
- Repositories: Abstractions that provide access to aggregates.
- Services: Operations that do not naturally fit within entities or value objects.
- Domain Events: Events that signify something important has happened within the domain.
Some Concepts about Layers and Components
Entity
Domain-Driven Design (DDD) → Used to represent domain objects.
Clean Architecture → Belongs to the domain layer (Core).
- Object with a unique identity.
- Mutable state over time.
- Compared by its identifier (id) and not by attribute values.
Value Object
Domain-Driven Design (DDD) → Used to maintain consistency between entities.
Principle of Immutability → Value objects should not be altered.
- Immutable object.
- Compared by its content, not by an id.
- Encapsulates specific logic within it.
export class Email {
private readonly value: string;
constructor(email: string) {
if (!this.isValidEmail(email)) {
throw new Error("Invalid email.");
}
this.value = email.toLowerCase();
}
private isValidEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
getValue(): string {
return this.value;
}
}
Aggregate
Domain-Driven Design (DDD) → Used to maintain consistency between entities.
Encapsulation → Only the aggregate root can modify its internal entities.
- Group of related entities treated as a single unit.
- Has an "aggregate root" that controls the other entities.
- Ensures internal consistency by allowing changes only through the root.
import { User } from "../entities/User";
export class Account {
constructor(private owner: User, private balance: number = 0) {}
deposit(amount: number) {
if (amount <= 0) throw new Error("Deposit must be greater than zero.");
this.balance += amount;
}
withdraw(amount: number) {
if (amount > this.balance) throw new Error("Insufficient balance.");
this.balance -= amount;
}
getBalance() {
return this.balance;
}
}
Domain Exceptions (exceptions/)
- Specific domain errors should have their own classes.
export class DomainException extends Error {
constructor(message: string) {
super(message);
this.name = "DomainException";
}
}
export class InsufficientBalanceException extends DomainException {
constructor() {
super("Insufficient balance.");
}
}
Interfaces
export interface IUserRepository {
save(user: User): Promise<void>;
findByEmail(email: string): Promise<User | null>;
}
- Domain-Driven Design (domain/contracts)
- They are in the domain layer because the domain cannot depend on external technologies.
- Interfaces are used to define repositories, domain services, and factories.
- Clean Architecture (application/interfaces)
- In Clean Architecture, this layer is usually called "Interfaces" or "Contracts".
- It defines Gateways, Repositories, and Services, which will be used by Use Cases.
- Hexagonal Architecture (core/ports)
- In Hexagonal Architecture, this layer is called "Ports".
- Ports define how the domain communicates with the external world, but without implementing anything.
Presentation
The Presentation layer is responsible for the application's input interface, meaning it receives user requests and translates them into use cases (Use Cases or Application Services).
📌 It appears in architectures like Clean Architecture and Hexagonal, it is not a central concept of DDD! 📌 It does NOT contain business rules! Its only role is to interpret inputs and direct them to the use cases. It can contain different types of input adapters, depending on the type of interface:
| Input Type | Example | | ----------- | --------------------------------------- | | REST API | Controllers (Express, Fastify, NestJS) | | GraphQL API | Resolvers (Apollo, Mercurius) | | CLI | Terminal commands (Commander.js, Yargs) | | WebSockets | Real-time connection management | | Messaging | Message queues (Kafka, RabbitMQ) |
Key Differences
DDD: Defines how to model the domain with well-structured business rules.
Clean Architecture: Defines the separation into layers to maintain low coupling.
Hexagonal Architecture: Defines how the domain communicates with the external world through ports & adapters.
Example Simple Structure DDD
src/
├── application/
│ ├── use-cases/
│ │ ├── create-user.use-case.ts
│ │ ├── update-user.use-case.ts
│ │ └── delete-user.use-case.ts
│ └── dto/
│ └── user-dto.ts
├── domain/
│ ├── entities/
│ │ └── user.ts
│ ├── value-objects/
│ │ └── email.ts
│ ├── aggregates/
│ │ └── user.aggregate.ts
│ ├── contracts/
│ │ └── user.contract.ts
│ ├── services/
│ │ └── user.service.ts
│ ├── events/
│ │ └── user.created.event.ts
│ └── exceptions/
│ └── UserNotFoundException.ts
├── infrastructure/
│ ├── config/
│ │ └── app-config.ts
│ ├── repositories
│ │ └── UserRepository.ts
│ ├── logger
│ │ └── logger.ts
│ ├── http
│ │ └── server.ts
│ ├── services
│ │ ├── auth.service.ts
│ │ └── email.service.ts
│ ├── database/
│ │ └── prisma/
│ │ ├── client.ts
│ │ └── user-repository.ts
├── shared/
│ ├── utils/
│ │ ├── date.util.ts
│ │ └── string.util.ts
│ ├── constants/
│ │ └── error-codes.ts
│ ├── mappers/
│ │ └── user.mapper.ts
│ ├── errors/
│ │ ├── app-error.ts
│ │ └── not-found-error.ts
│ └── middlewares/
│ ├── error-handler.middleware.ts
│ └── auth.middleware.ts
└── main.ts
Clean Architecture with Domain-Driven Design (DDD)
Combining Clean Architecture with Domain-Driven Design (DDD) provides a robust framework for building scalable and maintainable applications by clearly defining the boundaries between different parts of the system and focusing on the core business logic.
src/
├── application/
│ ├── dto/
│ │ ├── user-dto.ts
│ │ └── product-dto.ts
│ ├── services/
│ │ ├── notification.service.ts
│ │ └── cache.service.ts
│ └── use-cases/
│ └── user/
│ ├── create-user.use-case.ts
│ ├── update-user.use-case.ts
│ └── delete-user.use-case.ts
├── domain/
│ ├── entities/
│ │ ├── user.ts
│ │ └── product.ts
│ ├── interfaces/
│ │ ├── user-repository.ts
│ │ └── email-service.ts
│ ├── services/
│ │ ├── auth-service.ts
│ │ └── payment-service.ts
│ └── value-objects/
│ ├── email.ts
│ └── price.ts
├── infrastructure/
│ ├── cache/
│ │ └── redis.ts
│ ├── database/
│ │ └── prisma/
│ │ ├── client.ts
│ │ ├── migrations/
│ │ └── user.repository.ts
│ ├── http/
│ │ ├── controllers/
│ │ │ ├── user.controller.ts
│ │ │ └── product.controller.ts
│ │ ├── routes/
│ │ │ ├── user.routes.ts
│ │ │ └── product.routes.ts
│ │ └── interfaces/
│ │ └── http-server.ts
│ ├── queue/
│ │ ├── bull.config.ts
│ │ └── process-job.ts
│ ├── repositories/
│ │ └── user-repository.ts
│ ├── config/
│ │ └── app-config.ts
│ └── services/
│ ├── email.service.ts
│ └── logger.service.ts
├── presentaion/
│ ├── controllers/
│ │ ├── user.controller.ts
│ │ └── product.controller.ts
│ ├── routes/
│ │ ├── user.routes.ts
│ │ └── product.routes.ts
│ ├── middlewares/
│ │ ├── error-handler.middleware.ts
│ │ └── auth.middleware.ts
│ ├── graphql/
│ │ ├── resolvers/
│ │ │ ├── user.resolver.ts
│ │ │ └── product.resolver.ts
│ │ └── schema.ts
│ ├── websockets/
│ │ ├── socket-server.ts
│ │ └── socket-events.ts
│ └── cli/
│ └── user-cli.ts
├── shared/
│ ├── config/
│ │ └── env.ts
│ ├── errors/
│ │ ├── app-error.ts
│ │ └── not-found-error.ts
│ ├── mappers/
│ │ ├── user.mapper.ts
│ │ └── product.mapper.ts
│ ├── constants/
│ │ └── error-codes.ts
│ ├── middlewares/
│ │ ├── error-handler.middleware.ts
│ │ └── auth.middleware.ts
│ └── utils/
│ ├── date.util.ts
│ └── string.util.ts
└── main.ts
This structure ensures that the core business logic is isolated from external dependencies, making the codebase more maintainable, testable, and scalable. The use cases orchestrate the flow of data between the domain and external systems, while the infrastructure layer handles the implementation details of these systems.
Hexagonal Architecture
Hexagonal Architecture, also known as Ports and Adapters, is a design pattern that aims to create loosely coupled application components that can be easily connected to their software environment through ports and adapters. This architecture promotes separation of concerns, making the application more modular, testable, and maintainable.
In a Hexagonal Architecture, the core logic of the application is isolated from external systems such as databases, user interfaces, and third-party services. The core interacts with these external systems through well-defined interfaces (ports), and the actual implementation of these interfaces (adapters) can be swapped without affecting the core logic.
| Adapter Type | Function | Implementation Example | | ----------------- | ------------------------------------------ | ---------------------------------- | | Persistence | Save and retrieve data from the database | Prisma, Sequelize, MongoDB, Redis | | Controllers | Adapt HTTP input for use cases | Express, Fastify, NestJS, HapiJS | | Gateways | Communication with external services | Stripe, SendGrid, Twilio, Firebase | | Messaging | Asynchronous communication (message queue) | Kafka, RabbitMQ, AWS SQS | | CLI Adapters | Command line input (CLI) | Commander.js, yargs | | GraphQL Resolvers | Adapters for GraphQL | Apollo Server, Mercurius | | WebSockets | Real-time bidirectional communication | Socket.io, WebRTC |
When to Use Each Adapter?
Persistence: Use when the system needs to store data in databases like PostgreSQL, MongoDB, Redis.
Controllers: Use to expose REST endpoints.
Gateways: Use to consume external APIs (payments, emails, notifications).
Messaging: Use for asynchronous communication via queues (Kafka, RabbitMQ).
CLI: Use for terminal input (e.g., administrative scripts).
GraphQL: Use if the API uses GraphQL instead of REST.
WebSockets: Use for real-time events (chat, notifications).
🔥 Final Summary 📌 Adapters are all the concrete implementations that connect the domain to the external world. 📌 The domain should never depend directly on these implementations. 📌 Each type of adapter has a specific responsibility, avoiding excessive coupling.
Example Structure
src/
├── adapters/
│ ├── persistence
│ │ ├── PrismaUserRepository.ts
│ │ └── MongoUserRepository.ts
│ ├── controllers
│ │ └── UserController.ts
│ ├── gateways
│ │ ├── StripePaymentGateway.ts
│ │ └── SendGridEmailGateway.ts
│ ├── messaging
│ │ ├── KafkaQueueService.ts
│ │ └── RabbitMQQueueService.ts
│ ├── cli
│ │ └── UserCLI.ts
│ ├── graphql
│ │ └── UserResolver.ts
│ ├── websockets
│ │ └── WebSocketServer.ts
├── core/
│ ├── domain/
│ │ ├── entities/
│ │ │ └── user.ts
│ │ ├── value-objects/
│ │ │ └── email.ts
│ │ ├── aggregates/
│ │ │ └── user.aggregate.ts
│ │ └── domain-events/
│ │ ├── domain-event.ts
│ │ ├── user-created.event.ts
│ │ └── user-email-changed.event.ts
│ └── use-cases/
│ │ ├── create-user.use-case.ts
│ │ ├── update-user.use-case.ts
│ └── ports/
│ └── repositories/
│ └── user-repository.port.ts
├── infrastructure/
│ ├── http/
│ ├── database/
│ └── config/
└── main.ts
This structure ensures that the core business logic is independent of external systems, making it easier to test and maintain. The adapters are responsible for connecting the core to the external systems, allowing for flexibility and scalability in the application.
Summary of Differences
✅ DDD → Defines how to model the domain with well-structured business rules. ✅ Clean Architecture → Defines the separation into layers to maintain low coupling. ✅ Hexagonal Architecture → Defines how the domain communicates with the external world through ports & adapters.
| Feature | DDD | Clean Architecture | Hexagonal Architecture | | -------------- | ------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------ | | Main Focus | Domain modeling and business rules | Separation of concerns and minimal coupling | Isolation of domain from external dependencies | | Organization | Based on entities, aggregates, services | Based on independent layers | Based on Ports & Adapters pattern | | Use Cases | Apply specific business rules | Are the central layer of the system | Encapsulated in the domain layer | | Repositories | Part of the domain (interface and implementation) | Are adapters (not part of the core) | Defined as ports (interfaces), implemented by adapters | | Infrastructure | Concrete implementations within the domain | Infrastructure is separated | Implementations reside in Adapters layer | | Dependencies | May have coupling to ORM | Does not depend on external frameworks | Domain is framework-agnostic, interfaces bridge dependencies |
MVC Architecture
MVC (Model-View-Controller) is a software architectural pattern that separates an application into three main logical components: the Model, the View, and the Controller. Each of these components is built to handle specific development aspects of an application.
Model: Represents the data and the business logic of the application. It directly manages the data, logic, and rules of the application. The model responds to requests for information and updates from the view and instructions from the controller to update itself.
View: Represents the UI (User Interface) of the application. It displays the data from the model to the user and sends user commands to the controller. The view is responsible for rendering the model's data in a format suitable for interaction.
Controller: Acts as an intermediary between the model and the view. It listens to the input from the view, processes it (possibly altering the model's state), and returns the output display to the view. The controller handles the user input and updates the model and view accordingly.
Example Structure
src/
├── config/
│ ├── database.config.ts
│ └── app.config.ts
├── controllers/
│ ├── user.controller.ts
│ └── product.controller.ts
├── middlewares/
│ ├── auth.middleware.ts
│ └── error-handler.middleware.ts
├── models/
│ ├── user.model.ts
│ └── product.model.ts
├── views/
│ ├── user.view.ts
│ └── product.view.ts
├── routes/
│ ├── user.routes.ts
│ └── product.routes.ts
├── services/
│ ├── user.service.ts
│ └── product.service.ts
├── utils/
│ ├── date.util.ts
│ └── string.util.ts
└── main.ts
This structure ensures a clear separation of concerns, making the application easier to manage, test, and scale. The model handles the data and business logic, the view manages the presentation layer, and the controller processes user input and updates the model and view accordingly.
Serverless Architecture
Serverless Architecture is a cloud computing execution model where the cloud provider dynamically manages the allocation and provisioning of servers. In this architecture, developers can build and run applications without having to manage the underlying infrastructure. The term "serverless" is a bit of a misnomer, as servers are still involved, but the management of these servers is abstracted away from the developer.
In a serverless architecture, applications are typically composed of small, stateless functions that are triggered by events. These functions are often referred to as "Functions as a Service" (FaaS). The cloud provider automatically scales the functions in response to the number of incoming requests, ensuring that the application can handle varying levels of traffic without manual intervention.
Example Structure
src/
├── config/
│ ├── databaseConfig.ts
│ └── appConfig.ts
├── functions/
│ ├── createUser/
│ │ ├── handler.ts
│ │ ├── index.ts
│ │ └── schema.ts
│ ├── updateUser/
│ │ ├── handler.ts
│ │ ├── index.ts
│ │ └── schema.ts
│ └── deleteUser/
│ ├── handler.ts
│ ├── index.ts
│ └── schema.ts
├── models/
│ └── userModel.ts
├── services/
│ └── userService.ts
├── utils/
│ ├── dateUtil.ts
│ └── stringUtil.ts
└── serverless.yml
This structure ensures that the application is modular and each function is responsible for a specific task. The serverless.yml
file is used to define the configuration for the serverless framework, specifying the functions, events that trigger them, and the resources they require.
Serverless architecture offers several benefits, including reduced operational complexity, automatic scaling, and a pay-as-you-go pricing model. It is particularly well-suited for applications with unpredictable or fluctuating workloads, as it can efficiently handle varying levels of demand without the need for manual scaling.
Conclusion
The ts-node-app
CLI facilitates the creation and maintenance of project structures following well-defined architectural patterns. Use the commands above to efficiently manage your project's structure.