当前位置:首页 > 文章列表 > 文章 > 前端 > 清洁架构:遥不可及的理想——给开发者的寓言

清洁架构:遥不可及的理想——给开发者的寓言

来源:dev.to 2024-10-19 19:19:02 0浏览 收藏

今天golang学习网给大家带来了《清洁架构:遥不可及的理想——给开发者的寓言》,其中涉及到的知识点包括等等,无论你是小白还是老手,都适合看一看哦~有好的建议也欢迎大家在评论留言,若是看完有所收获,也希望大家能多多点赞支持呀!一起加油学习~

清洁架构:遥不可及的理想——给开发者的寓言

在西藏宁静的山区高处,一座古老寺院安静的大厅里,住着一位年轻的学徒。他致力于追求和谐——不仅在他自己内部,而且在他的编程技巧中。他的目标是创建一个完美的应用程序,一个能够体现清洁架构深刻原理的应用程序,就像山间溪流的清澈一样。但他意识到这条道路的艰辛,于是向一位可敬的明师寻求智慧。

徒弟谦卑地走近师父并问道:

—“噢,明智的师父,我已经构建了一个应用程序来管理购买。我的建筑干净纯粹吗?”

师父耐心地观察弟子,回答道:

—“向我展示你所创造的东西,我们将一起辨别真相。”

学徒展示了他的作品,其中数据库逻辑和用例流程交织在一起——业务逻辑与技术框架紧密地交织在一起,就像一张错综复杂的网中的线。

// app.ts
import sqlite3 from 'sqlite3';
import { open, database } from 'sqlite';

interface purchase {
    id: number;
    title: string;
    cost: number;
}

async function initializedatabase(): promise {
    const db = await open({
        filename: ':memory:',
        driver: sqlite3.database,
    });

    await db.exec(`
    create table purchases (
      id integer primary key,
      title text,
      cost real
    )
  `);

    return db;
}

async function addpurchaseifcan(db: database, purchase: purchase): promise {
    const { id, title, cost } = purchase;
    const row = await db.get<{ totalcost: number }>(
        `select sum(cost) as totalcost from purchases where title = ?`,
        [title]
    );
    const totalcost = row?.totalcost || 0;
    const newtotalcost = totalcost + cost;

    if (newtotalcost < 99999) {
        await db.run(
            `insert into purchases (id, title, cost) values (?, ?, ?)`,
            [id, title, cost]
        );
        console.log('purchase added successfully.');
    } else {
        console.log('total cost exceeds 99999.');
    }
}

(async () => {
    const db = await initializedatabase();
    await addpurchaseifcan(db, { id: 3, title: 'rice', cost: 2 });
})();

大师思考代码后说道:

——“你的代码就像一条河流,目的的清水与实现的泥浆混合在一起。业务和技术问题本应分开进行,但它们已合而为一。为了在你的建筑中实现真正的纯粹,你必须将它们分开,就像天空与大地分开一样。”

道路上的第一步

听从师父的话,徒弟开始着手重构他的代码。他开始分离各层,在数据库和业务逻辑流之间划出明显的界限。他还引入了接口,使他的代码与依赖倒置原则保持一致,这是清洁架构的神圣教义之一。现在,他的应用不再依赖于具体的实现,而是依赖于思想的抽象。

// app.ts

import { initializedatabase } from './db/init';
import { purchaserepository } from './db/purchaserepository';
import { addpurchaseifcan } from './usecases/addpurchaseifcan';

(async () => {
  const db = await initializedatabase();
  const purchaserepository = new purchaserepository(db);

  await addpurchaseifcan(purchaserepository, { id: 3, title: 'rice', cost: 2 });
})();
// usecases/addpurchaseifcan.ts

import { ipurchaserepository, purchase } from './ipurchaserepository';

export async function addpurchaseifcan(
  purchaserepository: ipurchaserepository,
  purchase: purchase
): promise {
  const { id, title, cost } = purchase;

  const totalcost = await purchaserepository.gettotalcostbytitle(title);
  const newtotalcost = totalcost + cost;

  if (newtotalcost < 99999) {
    await purchaserepository.add(purchase);
    console.log('purchase added successfully.');
  } else {
    console.log('total cost exceeds 99999.');
  }
}
// usecases/ipurchaserepository.ts

export interface ipurchaserepository {
  add(purchase: purchase): promise;
  gettotalcostbytitle(title: string): promise;
}

export interface purchase {
  id: number;
  title: string;
  cost: number;
}
// db/init.ts

import sqlite3 from 'sqlite3';
import { open, database } from 'sqlite';

export async function initializedatabase(): promise {
  const db = await open({
    filename: ':memory:',
    driver: sqlite3.database,
  });

  await db.exec(`
    create table purchases (
      id integer primary key,
      title text,
      cost real
    )
  `);

  return db;
}
// db/purchaserepository.ts

import { database } from 'sqlite';
import { ipurchaserepository, purchase } from 'usecases/ipurchaserepository';

export class purchaserepository implements ipurchaserepository {
  private db: database;

  constructor(db: database) {
    this.db = db;
  }

  async add(purchase: purchase): promise {
    const { id, title, cost } = purchase;
    await this.db.run(
      `insert into purchases (id, title, cost) values (?, ?, ?)`,
      [id, title, cost]
    );
    return purchase;
  }

  async gettotalcostbytitle(title: string): promise {
    const row = await this.db.get<{ totalcost: number }>(
      `select sum(cost) as totalcost from purchases where title = ?`,
      [title]
    );
    const totalcost = row?.totalcost || 0;
    return totalcost;
  }
}

徒弟回到师傅并问道:

——“我已经为我的存储库分离了各层并使用了接口。我的建筑现在干净了吗?”

大师再次检查代码,回复:

——“你们已经向前迈出了一步,但总成本的计算仍然停留在基础设施上,它不属于那里。这不是这种智慧应该存在的地方。总成本的知识属于商业领域,而不是地球上的工具。将其移至用例中,过程的智慧可以保持其纯粹性。”

分离的教训

有了这种洞察力,学徒意识到总成本的计算是业务逻辑的一部分。他再次重构了代码,将逻辑转移到用例中,使业务问题不受技术基础设施的影响。

// usecases/ipurchaserepository.ts

export interface ipurchaserepository {
  add(purchase: purchase): promise;
-  gettotalcostbytitle(title: string): promise;
+  getpurchasesbytitle(title: string): promise;
}
...
// usecases/addpurchaseifcan.ts

import { ipurchaserepository, purchase } from './ipurchaserepository';

export async function addpurchaseifcan(
  purchaserepository: ipurchaserepository,
  purchasedata: purchase,
  limit: number
): promise {
  const { id, title, cost } = purchasedata;

  const purchases = await purchaserepository.getpurchasesbytitle(title);

  let totalcost = 0;
  for (const purchase of purchases) {
    totalcost += purchase.cost;
  }

  const newtotalcost = totalcost + cost;

  if (newtotalcost >= limit) {
    console.log(`total cost exceeds ${limit}.`);
  } else {
    await purchaserepository.add(purchasedata);
    console.log('purchase added successfully.');
  }
}
// db/purchaserepository.ts

import { database } from 'sqlite';
import { ipurchaserepository } from './ipurchaserepository';

export class purchaserepository implements ipurchaserepository {

  ...

  async getpurchasesbytitle(title: string): promise {
    const rows = await this.db.all(
      `select * from purchases where title = ?`,
      [title]
    );
    return rows.map((row) => ({
      id: row.id,
      title: row.title,
      cost: row.cost,
    }));
  }
}

再次回到师父身边,问道:

—“我已将总成本计算转移到用例中,并将业务逻辑与基础设施分开。我的建筑现在纯净了吗?”

师父带着温柔的微笑回答:

——“你已经取得了很大的进步,但要小心——就像山风带来冬天的寒冷一样,你的计算可能会带来隐藏的错误。 javascript 的算术就像新手的思维一样,在处理大数或小数时可能会不精确。”

与无常的相遇

学徒明白javascript中浮点运算的缺陷可能会导致微妙但危险的错误。他修改了代码,转向更可靠的工具,一个专为精确计算而设计的库,寻求工作的清晰度。

// usecases/addpurchaseifcan.ts
+ import decimal from 'decimal.js';
import { ipurchaserepository, purchase } from './ipurchaserepository';

export async function addpurchaseifcan(
  purchaserepository: ipurchaserepository,
  purchasedata: purchase,
  limit: number
): promise {
  const { id, title, cost } = purchasedata;

  const purchases = await purchaserepository.getpurchasesbytitle(title);

  let totalcost = new decimal(0);
  for (const purchase of purchases) {
-    totalcost += purchase.cost;
+    totalcost = totalcost.plus(purchase.cost);
  }

- const newtotalcost = totalcost + cost;
+ const newtotalcost = totalcost.plus(cost);

- if (newtotalcost >= limit) {
+ if (newtotalcost.greaterthanorequalto(limit)) {
    console.log(`total cost exceeds ${limit}.`);
  } else {
    await purchaserepository.add(purchasedata);
    console.log('purchase added successfully.');
  }
}

他再次问师父:

—“我已经改进了我的计算,使用了更好的工具来避免错误。我的建筑现在达到纯粹了吗?”

上人目光坚定如山,答道:

——“你做得很好,但你的架构仍然受到束缚。您的业​​务逻辑现在取决于这个新工具decimal.js的详细信息。如果有一天你需要改变这个工具,你的逻辑基础就会动摇。真正的纯洁是摆脱这种束缚。”

依赖倒置的智慧

认识到大师话语的深度,学徒试图将他的代码从这种执着中解放出来。他抽象了算术运算,颠倒了依赖关系,这样他的业务逻辑就不再依赖于任何一种工具。

// usecases/calculator.ts
export abstract class calculator {
  abstract create(a: number): calculator;
  abstract add(b: calculator | number): calculator;
  abstract greaterthanorequal(b: calculator | number): boolean;
}
// usecases/addpurchaseifcan.ts
+ import { calculator } from 'usecases/calculator';
- import decimal from 'decimal.js';
import { ipurchaserepository, purchase } from './ipurchaserepository';
// decimalcalculator.ts

import decimal from 'decimal.js';
import { calculator } from 'usecases/calculator.ts';

export class decimalcalculator extends calculator {
  private value: decimal;

  constructor(value: number | decimal) {
    super();
    this.value = new decimal(value);
  }

  create(a: number): calculator {
    return new decimalcalculator(a);
  }

  add(b: calculator | number): calculator {
    return new decimalcalculator(this.value.plus(b.value));
  }

  greaterthanorequal(b: calculator | number): boolean {
    return this.value.greaterthanorequalto(b.value);
  }
}
// useCases/addPurchaseIfCan.ts

import { Calculator } from 'useCases/calculator';
import { IPurchaseRepository, Purchase } from './IPurchaseRepository';

export class addPurchaseIfCan {
  private purchaseRepository: IPurchaseRepository;
  private calculator: Calculator;
  private limit: string;

  constructor(
    purchaseRepository: IPurchaseRepository,
    calculator: Calculator,
    limit: number
  ) {
    this.purchaseRepository = purchaseRepository;
    this.calculator = calculator;
    this.limit = limit.toString();
  }

  async execute(purchaseData: Purchase): Promise {
    const { id, title, cost } = purchaseData;

    const purchases = await this.purchaseRepository.getPurchasesByTitle(title);

    let totalCost = this.calculator.create(0);
    for (const purchase of purchases) {
      totalCost.add(purchase.cost);
    }

    totalCost = totalCost.add(cost);

    if (totalCost.greaterThanOrEqual(this.limit)) {
      console.log(`Total cost exceeds ${limit}.`);
    } else {
      await this.purchaseRepository.add({
        id,
        title,
        cost: parseFloat(cost.toString()),
      });
      console.log('Purchase added successfully.');
    }
  }
}

最后一次回到师父身边,他问道:

——“我使用依赖倒置抽象了我的操作,确保我的业务逻辑不再与实现绑定。我的建筑现在真的干净了吗?”

师父开示:

——“这条路上你已经走得很远了。但请记住,即使您努力追求纯度,您的用例仍然取决于编写它们的语言。您现在使用的是 javascripttypescript,但有一天这些工具可能会消失。当那一天到来时,你会用新的语言重建一切吗?”

拥抱不完美

学徒对此感到困惑,问道:

——“大师,如果我的用例总是与编写它们的语言联系在一起,我怎样才能在我的架构中实现完美的整洁?”

师父带着理解的柔和微笑回答:

——“就像鸟儿无法离开天空一样,建筑也不能完全脱离其创作的工具。真正的独立是一个崇高的梦想,但却是遥不可及的。然而,对它的追求会给你的建筑带来和谐。 清洁架构的目标不是摆脱所有依赖性,而是创建一个轻松应对变化的系统,并将商业智慧与地球运作分开。理解这种平衡是获得真正智慧的关键。”

学徒,感受到他内心的理解黎明之光,说道:

——“谢谢师父。现在我发现完美不是孤立的,而是责任和目标的和谐。”

师父从座位上站起来,回答:

——“放心吧,学徒。你的旅程才刚刚开始,但你已经找到了自己的路。”

结语

随着时间的流逝,学徒注意到他的应用程序开始变慢。他很困惑,想知道一个曾经运行得如此顺利的系统现在如何在执行任务时陷入困境。

很明显,问题不在于代码的大小不断增长,而在于总成本计算是在数据库外部进行的。该应用程序花费了大量的精力来传输大量数据,只是为了执行本可以在源头完成的任务。如果计算是在数据库内完成的,则无需在层之间发送数千条记录,并且性能仍将保持强劲。

徒弟想向师父询问此事,但师父已经消失,问题一直没有答案。

望着寂静的寺院,徒弟拿起一本新书,微笑着说道:

—“看来我的启蒙之路给我带来了新的挑战 - 性能优化的艺术。”

本篇关于《清洁架构:遥不可及的理想——给开发者的寓言》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!

版本声明
本文转载于:dev.to 如有侵犯,请联系study_golang@163.com删除
探索 NFT 平台的发展和影响探索 NFT 平台的发展和影响
上一篇
探索 NFT 平台的发展和影响
产品交付进度延迟,天奥电子第三季度净利润同比预降99.24%–81.2%
下一篇
产品交付进度延迟,天奥电子第三季度净利润同比预降99.24%–81.2%
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 笔灵AI生成答辩PPT:高效制作学术与职场PPT的利器
    笔灵AI生成答辩PPT
    探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
    27次使用
  • 知网AIGC检测服务系统:精准识别学术文本中的AI生成内容
    知网AIGC检测服务系统
    知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
    42次使用
  • AIGC检测服务:AIbiye助力确保论文原创性
    AIGC检测-Aibiye
    AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
    39次使用
  • 易笔AI论文平台:快速生成高质量学术论文的利器
    易笔AI论文
    易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
    51次使用
  • 笔启AI论文写作平台:多类型论文生成与多语言支持
    笔启AI论文写作平台
    笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
    42次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码