我在制定我的IDP(个人发展计划)时学到的东西

发布: (2025年12月18日 GMT+8 18:38)
9 min read
原文: Dev.to

Source: Dev.to

(请提供您希望翻译的具体文本内容,我将按照要求保留源链接并翻译正文部分。)

定义

基于这些反思,我确定了最初的目标:

  1. 提升我的技术技能
  2. 用英文记录整个项目

确定主题并不简单,因为我试图兼顾自身成长以及 IDP 能为公司带来的价值。我希望它既实用,又能在日常工作中应用,并且帮助我在未来做出更有依据的架构决策。

我想到:作为全栈开发者不可能面面俱到,但掌握架构原则会产生决定性影响,无论使用何种技术栈。

于是,我选择在实践中学习并运用 SOLID(单一职责、开放‑封闭、里氏替换、接口分离和依赖倒置)。

基于此动机,我又确定了第三个目标:

  1. 成为能够在项目构思阶段应用最佳实践的开发者

我的主要动力来源于经验。在许多项目中,我没有导师,只能通过犯错并在最短时间内交付尽可能好的成果来学习。更好地理解 SOLID 将帮助我构建更有组织性和可扩展性的系统,直接提升我的工作质量。

规划

我整理了一份想要应用的技术栈和实践清单:

  1. Laravel – 使用 Eloquent(已经是我的日常)。我将侧重概念和架构,而不是重新发明已有的功能。
  2. Vue.js – 使用 Composition API。
  3. ESLint & Prettier – 确保代码遵循标准且不会触发 IDE 错误。
  4. 服务层 – 我原本不确定是使用服务层还是仓库模式;最终选择了服务层(下面有详细说明)。

并非我一开始定义的所有内容都已交付(例如身份验证、CORS、部署、git hooks)。我计划稍后实现这些功能,当前只专注于最佳实践和架构。

项目主题: 都市传说地图 – 一个用于实践最佳实践、提升我的 Laravel 与 Vue.js 技能、加深对 SOLID 原则理解的载体。

实践

为什么使用 Composition API?

我最初使用 Vue.js 的 Options API,但在一些项目中,复杂度随文件大小一起增长。 在进行中的项目中切换到 Composition API 让体验更顺畅,因为它有助于复用和组织。

将 SOLID 原则应用到前端并不明显,但我专注于职责分离和代码可复用性。

UI 组件应只处理 UI

我创建了一个 src/api/ 文件夹,包含:

src/api/
├─ connect.js   // configures the connection to the API
└─ legend.js    // queries the API endpoints

每个文件只有单一职责。

组件只依赖 legend.js 导出的函数,而不是直接依赖 Axios。若我将 Axios 换成 GraphQL,组件内部无需任何更改。

有了这种结构,UrbanLegendMap.vue 组件仅处理 UI(Leaflet),消费领域函数,并且不包含任何 HTTP 逻辑。

为什么使用 Service Layer?

在项目中更换 ORM 非常困难,因此实现 Repository Pattern 会增加不必要的层,并且会重复 Eloquent 已经提供的功能。

因此,Service Layer 负责业务决策、处理方式以及包含可复用的逻辑——即业务规则。

每个类应只有单一的修改理由

控制器只接收请求,调用服务接口,并返回 HTTP 响应,例如:

public function store(StoreUrbanLegendRequest $request)
{
    $legend = $this->service->create($request->validated());

    return (new UrbanLegendResource($legend))
        ->response()
        ->setStatusCode(Response::HTTP_CREATED);
}

控制器中不应包含验证逻辑或业务规则。

当创建新 legend 时,会为 URL 访问生成 slug。此职责交给 模型 处理。

因此:

  • 验证 → 表单请求
  • 业务规则 → Service
  • Slug 生成、UUID、关系 → Model

系统的每个部分现在都有明确的职责。

对扩展开放,对修改封闭

控制器仅依赖服务的 接口,而不是具体实现。如果以后将服务迁移到外部 API,只需创建另一个实现相同接口的类。控制器保持不变,控制器与新服务共享同一逻辑契约。

测试特性可能有益

我通常只测试服务或用例层,但由于项目结构良好,我同时添加了 特性测试单元测试,以验证整个栈的行为。

功能测试

我可以测试完整的流程——路由、中间件、请求、服务、模型和资源——例如:

public function test_validates_required_fields_and_returns_422(): void
{
    User::factory()->create();

    $res = $this->withHeaders([
        'Authorization' => 'Bearer ' . $this->token,
    ])->postJson('/api/legend', $this->payload([
        'title' => '',
    ]));

    $res->assertStatus(422)
        ->assertJsonValidationErrors(['title']);
}

这并不意味着我会始终进行功能测试,但由于我的前端完全依赖外部 API,验证响应符合 HTTP 原则并帮助我编写文档显得尤为重要。

单元测试

单元测试在隔离的情况下验证业务规则,例如:

public function test_list_returns_filtered_legends(): void
{
    $this->service->create($this->payload([
        'title' => 'Lenda - Brasília',
        'city'  => 'Brasília',
    ]));

    $this->service->create($this->payload([
        'title' => 'Lenda - Florianópolis',
        'city'  => 'Florianópolis',
    ]));

    $results = $this->service->list(['city' => 'Brasília']);

    $this->assertCount(1, $results);
    $this->assertEquals('Brasília', $results->first()->city);
}

IDP 如何改变了我的思考

在整个个人发展项目(IDP)期间,我的主要目标是提升我的技术知识。我:

  • 学习每一步和实现细节,追问每行代码背后的 why
  • 查阅大量文章,获取理论与实践示例。
  • 构建了一个运用 SOLID 原则的项目,发现每一个架构决策都会影响整个系统。

新的每日提问

在编写代码之前,我现在会自问:

  1. 谁负责这部分?
  2. 这个类是否只有单一的变更原因?
  3. 是否存在不必要的方法或函数?
  4. 能否在不破坏现有代码的前提下进行扩展?
  5. 维护时会不会让我头疼?

这些问题已经成为我的日常习惯。把 SOLID 作为我的主要主题,让我形成了一种思维方式,这种方式将在我的职业生涯中伴随我,超越本次 IDP。

虽然并未完成所有计划项目(例如认证功能),我仍然认为 IDP 是成功的。它让我获得了技术成熟度,并对如何从构思阶段起结构化项目有了扎实的理解——这不仅是学习一门技术,更是学会有意识地思考架构。

要点

  • 良好的实践不取决于技术栈,而取决于有意识的决策。
  • 演进仍在继续。

项目仓库:

参考文献

Back to Blog

相关文章

阅读更多 »

Go 中的依赖注入,简化为字段标签

核心理念:所有依赖注入都是使用结构体字段标签声明的,除此之外没有其他。 - 没有 provider sets。 - 没有 DSL。 - 没有运行时反射。 容器……

里氏替换原则,育儿模型

《Liskov Substitution Principle, A Model for Parenting》封面图片 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format...