AI 时代单体应用将重新焕发青春
在软件开发领域,AI 正在掀起一场革命性的变革。最近,我观看了一个令人印象深刻的国外技术分享,展示了一个完整的 AI 辅助编程实践案例。这个案例与以往那些简单的 AI 生成小游戏或 PPT 的演示完全不同,它展示了 AI 在实际业务系统开发中的强大潜力。
在软件开发领域,AI 正在掀起一场革命性的变革。最近,我观看了一个令人印象深刻的国外技术分享,展示了一个完整的 AI 辅助编程实践案例。这个案例与以往那些简单的 AI 生成小游戏或 PPT 的演示完全不同,它展示了 AI 在实际业务系统开发中的强大潜力。
相信每一个开发者在面对滚滚的 AI 大潮来袭的时候,心里都是打鼓的。我们别无选择,只能融入进去,感知这次大潮的跌宕起伏,一起成长。
在这个时间点上,AI 已经不是过去 Copilot 的补全代码,或者从 GPT Copy 代码这种范式了。而是无限接近自然语言编程,或者叫陪伴式编程。真正编程的已经是 AI,而不再是人类开发者。
这是一个基于 reveal.js 为 Logseq 创建幻灯片的插件。
jkhl
上下或左右切换幻灯片,b
切换黑屏。在标题栏中显示图标题和一些快速操作。
Logseq 终端是 Logseq 的一个终端模拟器。它可以在终端中执行各种操作,例如 cat
、ls
、cd
、tree
、pwd
等。这些命令与 Linux 中的真实终端命令类似,但并不完全相同。你可以使用 help
和 command --help
来了解更多信息。
cd
、ls
、cat
、pwd
、tree
、clear
等 20 多条命令。ctrl+p
/ctrl+n
进行导航。cd
、cat
、ls
等命令的命名空间自动补全。ctrl+a
/ctrl+e
、ctrl+d
/ctrl+h
、ctrl+f
/ctrl+b
、ctrl+k
/ctrl+u
、ctrl+l
等。如果你对这个插件感兴趣,可以在 Logseq 插件市场搜索安装。
在 Node.js 生态系统中,Express 和 Koa 等轻量级框架凭借其简洁灵活的特性,长期占据主导地位。然而,随着企业级应用的规模和复杂度不断提升,这些框架在工程化、可维护性和可扩展性方面的局限性逐渐显现。在这样的背景下,Nest.js 应运而生,它为 Node.js 开发带来了企业级框架所需的结构化和工程化体验。对于熟悉 Java Spring 或 PHP Laravel 等成熟企业级框架的开发者而言,Nest.js 的架构理念和开发体验会让你倍感亲切。
Nest.js 巧妙地整合了现代后端开发的精髓:采用 Express/Fastify 作为底层 HTTP 引擎,确保了高性能的请求处理能力;深度集成 TypeScript,带来了强大的类型安全和开发体验;引入依赖注入和模块化设计,实现了优雅的代码组织和解耦;同时还包含了大量现代后端开发的最佳实践,如面向切面编程(AOP)、依赖倒置原则(DIP)等。这些特性的组合,使得 Nest.js 成为构建企业级 Node.js 应用的理想选择。
Nest.js 的核心设计理念是"模块即应用"(Module as Application)。每个使用 @Module()
装饰器标注的类都代表一个功能完备的子系统,这种设计方式不仅提供了清晰的代码组织结构,还实现了真正的"关注点分离":
@Module({
imports: [DatabaseModule, AuthModule], // 依赖的其他模块
controllers: [UserController], // API 端点
providers: [UserService], // 业务逻辑
exports: [UserService], // 暴露给其他模块的服务
})
export class UserModule {}
这种模块化设计让应用系统像搭建乐高积木一样灵活可组合。在大型应用中,我们可以将系统优雅地拆分为数十个功能模块,每个模块都遵循单一职责原则(SRP),并通过定义良好的接口与其他模块进行交互。这种设计不仅提高了代码的可维护性和可测试性,还为后续的微服务改造提供了天然的基础。
Nest.js 的依赖注入(Dependency Injection,DI)系统是其最具特色和强大的特性之一。它不仅简化了对象的创建和管理,还为应用提供了极大的灵活性和可测试性:
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
private configService: ConfigService
) {}
async createUser(dto: CreateUserDto) {
// 业务逻辑
}
}
通过构造函数注入的方式,Nest.js 优雅地解决了对象依赖的问题。你不再需要手动实例化对象或管理依赖关系,这些都交由 Nest.js 的控制反转(Inversion of Control,IoC)容器来处理。IoC 容器不仅负责对象的创建和注入,还精确管理着这些对象的整个生命周期,从而实现了组件间的真正松耦合。这种设计极大地提升了代码的可测试性和可维护性,因为你可以轻松地替换或模拟任何依赖组件。
Nest.js 推崇经典的三层架构模式,这种架构模式经过了时间的检验,在企业级应用开发中被广泛采用:
这种清晰的分层架构带来了诸多优势:代码职责明确,易于测试和维护,且具有极强的灵活性。例如,你可以在不影响业务逻辑的情况下,轻松地替换底层的数据访问实现:
// 使用 TypeORM 的实现
@Injectable()
export class DatabaseUserRepository implements UserRepository {
// 实现接口方法
}
// 使用 MongoDB 的实现
@Injectable()
export class MongoUserRepository implements UserRepository {
// 同样的接口,不同实现
}
拦截器(Interceptor)是 Nest.js 实现面向切面编程(AOP)的核心机制,它允许你:
以下是一个性能监控拦截器的实现示例:
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest();
const method = request.method;
const url = request.url;
const now = Date.now();
return next.handle().pipe(
tap(() => {
this.logger.log(`${method} ${url} ${Date.now() - now}ms`);
})
);
}
}
这种面向切面编程的能力让你能够优雅地实现各种横切关注点(Cross-cutting Concerns):
管道(Pipe)是 Nest.js 中处理输入数据验证和转换的核心机制。它不仅提供了强大的数据验证能力,还能确保数据类型的安全性:
@Post('users')
@UsePipes(new ValidationPipe({
transform: true, // 自动转换类型
whitelist: true, // 过滤掉未定义的属性
forbidNonWhitelisted: true, // 发现未定义属性时抛出错误
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
}))
async createUser(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
结合 class-validator 和 class-transformer,你可以构建强大而优雅的数据验证模型:
export class CreateUserDto {
@IsEmail()
@Transform(({ value }) => value.toLowerCase())
email: string;
@MinLength(8)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {
message: "密码必须包含大小写字母和数字",
})
password: string;
@IsOptional()
@Type(() => Date)
@IsDate()
birthday?: Date;
@IsString({ each: true })
@ArrayMinSize(1)
roles: string[];
}
这种声明式的验证方式不仅提高了代码的可读性,还确保了数据的完整性和类型安全。
Nest.js 提供了全面的微服务支持,它不仅支持多种传输层协议,还提供了完整的微服务开发工具集:
// main.ts
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.KAFKA,
options: {
client: {
brokers: ["kafka:9092"],
clientId: "user-service",
},
consumer: {
groupId: "user-consumer-group",
},
},
}
);
// user.controller.ts
@Controller()
export class UserController {
@MessagePattern("create_user")
async createUser(@Payload() data: CreateUserDto) {
return this.userService.create(data);
}
@EventPattern("user_created")
async handleUserCreated(@Payload() data: any) {
// 处理用户创建事件
}
}
Nest.js 支持多种微服务通信模式:
这种灵活的协议支持让你能够根据具体场景选择最适合的通信方式,构建高效的分布式系统。
Nest.js 推荐使用 @nestjs/config
模块进行配置管理,它提供了强大的配置管理功能:
// config/database.config.ts
export default registerAs("database", () => ({
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10) || 5432,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
}));
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: [
`.env.${process.env.NODE_ENV}.local`,
`.env.${process.env.NODE_ENV}`,
".env.local",
".env",
],
load: [databaseConfig],
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid("development", "production", "test")
.default("development"),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().default(5432),
}),
}),
],
})
export class AppModule {}
在服务中优雅地使用配置:
@Injectable()
export class DatabaseService {
constructor(
@Inject(databaseConfig.KEY)
private dbConfig: ConfigType<typeof databaseConfig>,
private configService: ConfigService
) {}
getConnection() {
// 使用类型安全的配置访问
return {
host: this.dbConfig.host,
port: this.dbConfig.port,
// 支持配置嵌套和默认值
debug: this.configService.get("database.debug", false),
};
}
}
实现统一的异常处理和响应格式:
// interfaces/api-response.interface.ts
export interface ApiResponse<T = any> {
code: number;
message: string;
data?: T;
timestamp: string;
path: string;
}
// filters/http-exception.filter.ts
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GlobalExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException ? exception.message : "服务器内部错误";
// 记录错误日志
this.logger.error(
`${request.method} ${request.url}`,
exception instanceof Error ? exception.stack : "未知错误"
);
const responseBody: ApiResponse = {
code: status,
message,
timestamp: new Date().toISOString(),
path: request.url,
};
response.status(status).json(responseBody);
}
}
// 全局注册
app.useGlobalFilters(new GlobalExceptionFilter());
Nest.js 默认使用 Express,但在追求极致性能时,可以无缝切换到 Fastify:
// main.ts
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({
logger: true,
// 启用压缩
compression: true,
// 优化 JSON 解析
bodyLimit: 10485760, // 10MB
})
);
// 启用全局压缩
await app.register(compression, { encodings: ["gzip", "deflate"] });
// 配置全局前缀
app.setGlobalPrefix("api/v1");
// 启用 CORS
app.enableCors({
origin: ["http://localhost:3000"],
credentials: true,
});
// 配置 Swagger
const config = new DocumentBuilder()
.setTitle("API 文档")
.setVersion("1.0")
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("docs", app, document);
await app.listen(3000, "0.0.0.0");
}
Fastify 不仅提供了显著的性能提升,还带来了一些额外的优势:
Nest.js 提供了丰富的官方模块:
@nestjs/typeorm
/ @nestjs/mongoose
:数据库集成@nestjs/jwt
:认证支持@nestjs/schedule
:任务调度@nestjs/websockets
:实时通信@nestjs/graphql
:GraphQL 支持活跃的社区贡献了大量优质模块:
nestjsx/crud
:快速构建 CRUD APInestjs/config
:配置管理nestjs/terminus
:健康检查nestjs/prom
:监控指标也就是 Monorepo 模式,允许开发者在一个仓库中管理多个应用和共享库,适合需要代码共享和集中管理的场景。
在 Nest.js 上下文中,Monorepo 允许:
创建多个独立的 Nest.js 应用(如用户服务、订单服务)。
创建共享库(libs),这些库可被多个应用复用,如 DTOs(数据传输对象)、类型定义或通信库。
所有应用和库共享同一个 node_modules 目录和配置文件(如 tsconfig.json 和 nest-cli.json)。
以下是实际操作的详细步骤,基于官方文档和社区实践 [3][4]:
安装 Nest.js CLI: 运行以下命令全局安装:
npm i -g @nestjs/cli
创建新项目: 用 CLI 创建一个新的 Nest.js 项目:
nest new my-monorepo
这会生成一个标准的 Nest.js 应用。
添加第二个应用: 用以下命令生成第二个应用:
nest g app my-second-app
这会:
删除原来的 src 目录。
创建一个 apps 目录,包含 my-monorepo 和 my-second-app。 所有应用共享 node_modules 和配置文件。
添加共享库: 如果需要共享逻辑(如认证服务),用以下命令:
nest g lib my-shared-lib
这会在 libs 目录下创建 my-shared-lib,可被多个应用导入。
构建和运行:
构建特定应用或库:
nest build my-second-app
运行特定应用:
nest start my-second-app
注意:库不能独立运行,因为没有 main.ts 文件 [4]。
配置管理: 在 Monorepo 中,nest-cli.json 存储元数据,用于构建和组织工作空间项目。通常无需手动编辑,除非需要更改默认文件名。
在一个复杂的项目中,一个业务通常需要多个后台来支持,比如我有一个项目,需要一个 backend(业务系统后台), 一个 receiver(处理下游回调),一个 gateway (处理下游对接),一个 cronjob(计划任务),一个 processor(处理消息队列)。这些都是后端系统,处理的数据也都在一个数据库,但是业务逻辑是有很明显的分解的,比如 backend 是基于 Token 验证,gateway 是基于签名验证的,这些业务逻辑混在一起就容易引起混乱,彻底分开又会造成很多冗余代码,不利于维护。此时这种 monorepo 的方式就是一个很不错的解决方案。
Nest.js 正在成为 Node.js 后端开发的事实标准框架之一。它的设计哲学——"约定优于配置"、"模块化"、"可扩展性",使其在保持灵活性的同时提供了足够的结构。随着 Node.js 在企业中的普及,Nest.js 的这种平衡使其成为构建可维护、可扩展后端系统的理想选择。
这是一个命令行开发工具类的项目,基于 yargs
, 一般来说,如果你有一个想法,想写一个命令行工具,接收一些参数,根据参数做一些响应,这时你可以直接基于 yargs
或者 commander
库来写,难度也不大,问题是当你要写很多很多命令行工具,而且这些命令行工具是业务相关的,分布在公司内部不同的微服务里面,这时你不仅需要实现功能,还需要定一个规范,不然不同的命令行工具用不同的方法来实现,维护起来就是一场灾难,Semo.js
这个项目就是干这个用的。
这是一个为 Logseq 提供画笔功能的插件。
点击画笔图标以打开画笔模式,然后可以在其上绘制任何支持的样式,你可以将透明背景切换为白色。
如果你对这个插件感兴趣,可以在 Logseq 插件市场搜索安装。
这个插件从页面、块或标签中随机选择块。
直接使用渲染器语法:{{renderer random-block, [[xxx]]|((yyy))|#zzz, [size=1], [extra='']
或使用斜杠命令:Random Block
这是一个用于设置 Logseq 侧边栏预设的插件。
Logseq的侧边栏是一个动态区域,有时你希望保持一些页面或块始终显示在顶部并按特定顺序显示。这个插件可以帮助你实现。
如果你对这个插件感兴趣,可以在 Logseq 插件市场搜索并使用。