02
3

There has been a lot of confusion about design and architecture over the years. What is design? What is architecture? What are the differences between the two?

一直以来,设计(Design)与架构(Architecture)这两个概念让大多数人十分迷惑——什么是设计?什么是架构?二者究竟有什么区别?

One of the goals of this book is to cut through all that confusion and to define, once and for all, what design and architecture are. For starters, I’ll assert that there is no difference between them. None at all.

本书的一个重要的目标就是要清晰、明确地对二者进行定义。首先我要明确地说,二者没有任何区别。一丁点区别都没有!

The word “architecture” is often used in the context of something at a high level that is divorced from the lower-level details, whereas “design” more often seems to imply structures and decisions at a lower level. But this usage is nonsensical when you look at what a real architect does.

“架构”这个词往往使用于“高层级”的讨论中。这类讨论一般都把“底层”的实现细节排除在外。而“设计”一词,往往用来指代具体的系统底层组织结构和实现的细节。但是,从一个真正的系统架构师的日常工作来看,这样的区分是根本不成立的。

Consider the architect who designed my new home. Does this home have an architecture? Of course it does. And what is that architecture? Well, it is the shape of the home, the outward appearance, the elevations, and the layout of the spaces and rooms. But as I look through the diagrams that my architect produced, I see an immense number of low-level details. I see where every outlet, light switch, and light will be placed. I see which switches control which lights. I see where the furnace is placed, and the size and placement of the water heater and the sump pump. I see detailed depictions of how the walls, roofs, and foundations will be constructed.

以给我设计新房子的建筑设计师要做的事情为例。新房子当然是存在着既定架构的,但这个架构具体包含哪些内容呢?首先,它应该包括房屋的形状、外观设计、垂直高度、房间的布局,等等。但是,如果查看建筑设计师使用的图纸,会发现其中也充斥着大量的设计细节。譬如,我们可以看到每个插座、开关以及每个电灯具体的安装位置,同时也可以看到某个开关与所控制的电灯的具体连接信息;我们也能看到壁炉的具体安装位置,热水器的大小和位置信息,甚至是污水泵的位置;同时也可以看到关于墙体、屋顶和地基都有非常详细的建造说明。

In short, I see all the little details that support all the high-level decisions. I also see that those low-level details and high-level decisions are part of the whole design of the house.

总的来说,架构图里实际上包含了所有的底层设计细节,这些细节信息共同支撑了顶层的架构设计,底层设计信息和顶层架构设计共同组成了整个房屋的架构文档。

And so it is with software design. The low-level details and the high-level structure are all part of the same whole. They form a continuous fabric that defines the shape of the system. You can’t have one without the other; indeed, no clear dividing line separates them. There is simply a continuum of decisions from the highest to the lowest levels.

软件设计也是如此。底层设计细节和高层架构信息是不可分割的。它们组合在一起,共同定义了整个软件系统,缺一不可。所谓的底层和高层本身就是一系列决策组成的连续体,并没有清晰的分界线。

THE GOAL? 目标是什么
And the goal of those decisions? The goal of good software design? That goal is nothing less than my utopian description:

所有这些决策的终极目标是什么呢? 一个好的软件设计的终极目标是什么呢?就像我之前描述过的:

The goal of software architecture is to minimize the human resources required to build and maintain the required system.

软件架构的终极目标是,用最小的人力成本来满足构建和维护该系统的需求。

The measure of design quality is simply the measure of the effort required to meet the needs of the customer. If that effort is low, and stays low throughout the lifetime of the system, the design is good. If that effort grows with each new release, the design is bad. It’s as simple as that.

一个软件架构的优劣,可以用它满足用户需求所需要的成本来衡量。如果该成本很低,并且在系统的整个生命周期内一直都能维持这样的低成本,那么这个系统的设计就是优良的。如果该系统的每次发布都会提升下一次变更的成本,那么这个设计就是不好的。就这么简单。

CASE STUDY 案例分析
As an example, consider the following case study. It includes real data from a real company that wishes to remain anonymous.

下面来看一个真实案例,该案例中的数据均来源于一个要求匿名的真实公司。

First, let’s look at the growth of the engineering staff. I’m sure you’ll agree that this trend is very encouraging. Growth like that shown in Figure 1.1 must be an indication of significant success!

首先,我们来看一下工程师团队规模的增长。你肯定认为这个增长趋势是特别可喜的,像图 1.1 中的这种增长线条一定是公司业务取得巨大成功的直观体现。

Growth of the engineering staff

Reproduced with permission from a slide presentation by Jason Gorman

Now let’s look at the company’s productivity over the same time period, as measured by simple lines of code (Figure 1.2).

现在再让我们来看一下整个公司同期的生产效率(productivity),这里用简单的代码行数作为指标(参见图 1.2)。

Productivity over the same period of time

Clearly something is going wrong here. Even though every release is supported by an ever-increasing number of developers, the growth of the code looks like it is approaching an asymptote.

这明显是有问题的。伴随着产品的每次发布,公司的工程师团队在持续不断地扩展壮大,但是仅从代码行数的增长来看,该产品却正在逐渐陷入困境。

Now here’s the really scary graph: Figure 1.3 shows how the cost per line of code has changed over time.

还有更可怕的:图 1.3 展示的是同期内每行代码的变更成本。

These trends aren’t sustainable. It doesn’t matter how profitable the company might be at the moment: Those curves will catastrophically drain the profit from the business model and drive the company into a stall, if not into a downright collapse.

显然,图中展示的趋势是不可持续的。不管公司现在的利润率有多高,图中线条表明,按这个趋势下去,公司的利润会被一点点榨干,整个公司会因此陷入困境,甚至直接关门倒闭。

What caused this remarkable change in productivity? Why was the code 40 times more expensive to produce in release 8 as opposed to release 1?

究竟是什么因素造成生产力的大幅变化呢?为什么第 8 代产品的构建成本要 比第 1 代产品高 40 倍?

Cost per line of code over time

THE SIGNATURE OF A MESS 乱麻系统的特点
What you are looking at is the signature of a mess. When systems are thrown together in a hurry, when the sheer number of programmers is the sole driver of output, and when little or no thought is given to the cleanliness of the code or the structure of the design, then you can bank on riding this curve to its ugly end.

我们在这里看到的是一个典型的乱麻系统。这种系统一般都是没有经过设计,匆匆忙忙被构建起来的。然后为了加快发布的速度,拼命地往团队里加入新人,同时加上决策层对代码质量提升和设计结构优化存在着持续的、长久的忽视,这种状态能持续下去就怪了。

Figure 1.4 shows what this curve looks like to the developers. They started out at nearly 100% productivity, but with each release their productivity declined. By the fourth release, it was clear that their productivity was going to bottom out in an asymptotic approach to zero.

图 1.4 展示了系统开发者的切身体会。他们一开始的效率都接近 100%,然而伴随着每次产品的发布,他们的生产力直线下降。到了产品的第 4 版本时,很明显大家的生产力已经不可避免地趋近为零了。

Productivity by release

From the developers’ point of view, this is tremendously frustrating, because everyone is working hard. Nobody has decreased their effort.

对系统的开发者来说,这会带来很大的挫败感,因为团队中并没有人偷懒,每个人还都是和之前一样在拼命工作。

And yet, despite all their heroics, overtime, and dedication, they simply aren’t getting much of anything done anymore. All their effort has been diverted away from features and is now consumed with managing the mess. Their job, such as it is, has changed into moving the mess from one place to the next, and the next, and the next, so that they can add one more meager little feature.

然而,不管他们投入了多少个人时间,救了多少次火,加了多少次班,他们的产出始终上不去。工程师的大部分时间都消耗在对现有系统的修修补补上,而不是真正完成实际的新功能。这些工程师真正的任务是:拆了东墙补西墙,周而往复,偶尔有精力能顺便实现一点小功能。

THE EXECUTIVE VIEW 管理层视角
If you think that’s bad, imagine what this picture looks like to the executives! Consider Figure 1.5, which depicts monthly development payroll for the same period.

如果你觉得开发者们这样就已经够苦了,那么就再想想公司髙管们的感受吧! 请看图 1.5,该部门月工资同期图。

Monthly development payroll by release

Release 1 was delivered with a monthly payroll of a few hundred thousand dollars. The second release cost a few hundred thousand more. By the eighth release monthly payroll was $20 million, and climbing.

如你所见,产品的第 1 版是在月总工资 10 万美元左右的时候上线的。第 2 版又花掉了几十万美元。当发布第 8 版的时候,部门月工资已经达到了 2 千万美元,而且还在持续上升。

Just this chart alone is scary. Clearly something startling is happening. One hopes that revenues are outpacing costs and therefore justifying the expense. But no matter how you look at this curve, it’s cause for concern.

也许我们可以指望该公司的营收增长远远超出成本增长,这样公司就还能维持正常运转。但是这么惊人的曲线还是值得我们深入挖掘其中存在的巨大问题的。

But now compare the curve in Figure 1.5 with the lines of code written per release in Figure 1.2. That initial few hundred thousand dollars per month bought a lot of functionality—but the final $20 million bought almost nothing! Any CFO would look at these two graphs and know that immediate action is necessary to stave off disaster.

现在,只要将图 1.5 的月工资曲线和图 1.2 的每次发布代码行数曲线对比一下,任何一个理性的 CEO 都会一眼看出其中的问题:最开始的十几万美元工资给公司带来了很多新功能、新收益,而最后的 2 千万美元几乎全打了水漂。应立刻采取行动解决这个问题,刻不容缓。

But which action can be taken? What has gone wrong? What has caused this incredible decline in productivity? What can executives do, other than to stamp their feet and rage at the developers?

但是具体采取什么样的行动才能解决问题呢?究竟问题出在哪里?是什么造成了工程师生产力的直线下降?高管们除了跺脚、发飙,还能做什么呢?

WHAT WENT WRONG? 问题到底在哪里
Nearly 2600 years ago, Aesop told the story of the Tortoise and the Hare. The moral of that story has been stated many times in many different ways:

大约 2600 年前,《伊索寓言》里写到了龟兔赛跑的故事。这个故事的主题思想可以归纳为以下几种:

“Slow and steady wins the race.”
“The race is not to the swift, nor the battle to the strong.”
“The more haste, the less speed.”
慢但是稳,是成功的秘诀。
该比赛并不是拼谁开始跑得快,也不是拼谁更有力气的。
心态越急,反而跑得越慢。
The story itself illustrates the foolishness of overconfidence. The Hare, so confident in its intrinsic speed, does not take the race seriously, and so naps while the Tortoise crosses the finish line.

这个故事本身揭露的是过度自信的愚蠢行为。兔子由于对自己速度的过度自信,没有把乌龟当回事,结果乌龟爬过终点线取得胜利的时候,它还在睡觉。

Modern developers are in a similar race, and exhibit a similar overconfidence. Oh, they don’t sleep—far from it. Most modern developers work their butts off. But a part of their brain does sleep—the part that knows that good, clean, well-designed code matters.

这和现代软件研发工作有点类似,现在的软件研发工程师都有点过于自信。哦,当然,他们确实不会偷懒,一点也不。但是他们真正偷懒的地方在于——持续低估那些好的、良好设计的、整洁的代码的重要性。

These developers buy into a familiar lie: “We can clean it up later; we just have to get to market first!” Of course, things never do get cleaned up later, because market pressures never abate. Getting to market first simply means that you’ve now got a horde of competitors on your tail, and you have to stay ahead of them by running as fast as you can.

这些工程师们普遍用一句话来欺骗自己:“我们可以未来再重构代码,产品上线最重要!”但是结果大家都知道,产品上线以后重构工作就再没人提起了。市场的压力永远也不会消退,作为首先上市的产品,后面有无数的竞争对手追赶,必须要比他们跑得更快才能保持领先。

And so the developers never switch modes. They can’t go back and clean things up because they’ve got to get the next feature done, and the next, and the next, and the next. And so the mess builds, and productivity continues its asymptotic approach toward zero.

所以,重构的时机永远不会再有了。工程师们忙于完成新功能,新功能做不完,哪有时间重构老的代码?循环往复,系统成了一团乱麻,主产效率持续直线下降,直至为零。

Just as the Hare was overconfident in its speed, so the developers are overconfident in their ability to remain productive. But the creeping mess of code that saps their productivity never sleeps and never relents. If given its way, it will reduce productivity to zero in a matter of months.

结果就像龟兔赛跑中过于自信的兔子一样,软件研发工程师们对自己保持高产出的能力过于自信了。但是乱成一团的系统代码可没有休息时间,也不会放松。如果不严加提防,在几个月之内,整个研发团队就会陷入困境。

The bigger lie that developers buy into is the notion that writing messy code makes them go fast in the short term, and just slows them down in the long term. Developers who accept this lie exhibit the hare’s overconfidence in their ability to switch modes from making messes to cleaning up messes sometime in the future, but they also make a simple error of fact. The fact is that making messes is always slower than staying clean, no matter which time scale you are using.

工程师们经常相信的另外一个错误观点是: “在工程中容忍糟糕的代码存在可以在短期内加快该工程上线的速度,未来这些代码会造成一些额外的工作量,但是并没有什么大不了。”相信这些鬼话的工程师对自己清理乱麻代码的能力过于自信了。但是更重要的是,他们还忽视了一个自然规律:无论是从短期还是长期来看,胡乱编写代码的工作速度其实比循规蹈矩更慢。

Consider the results of a remarkable experiment performed by Jason Gorman depicted in Figure 1.6. Jason conducted this test over a period of six days. Each day he completed a simple program to convert integers into Roman numerals. He knew his work was complete when his predefined set of acceptance tests passed. Each day the task took a little less than 30 minutes. Jason used a well-known cleanliness discipline named test-driven development (TDD) on the first, third, and fifth days. On the other three days, he wrote the code without that discipline.

图 1.6 展示的是 Jason Gorman 进行的一次为期 6 天的实验。在该实验中,Jason 每天都编写一段代码,功能是将一个整数转化为相应罗马数字的字符串。当事先定义好的一个测试集完全通过时,即认为当天工作完成。每天实验的时长不超过 30 分钟。第一天、第三天和第五天,Jason 在编写代码的过程中采用了业界知名的优质代码方法论:测试驱动开发(TDD),而其他三天他则直接从头开始编写代码。

Time to completion by iterations and use/non-use of TDD

First, notice the learning curve apparent in Figure 1.6. Work on the latter days is completed more quickly than the former days. Notice also that work on the TDD days proceeded approximately 10% faster than work on the non-TDD days, and that even the slowest TDD day was faster than the fastest non-TDD day.

首先,我们要关注的是图 1.6 中那些柱状图。很显然,日子越往后,完成工作所需的时间就越少。同时,我们也可以看到当人们采用了 TDD 方法编程后,一般就会比未采用 TDD 方法编程少用 10%的时间,并且采用 TDD 方法编程时最差的一天也比未采用 TDD 方法编程时最好的一天用时要短。

Some folks might look at that result and think it’s a remarkable outcome. But to those who haven’t been deluded by the Hare’s overconfidence, the result is expected, because they know this simple truth of software development:

对于这个结果,有些人可能会觉得挺意外的。但是对常年关注软件开发本质的人来说,它其实揭示了软件开发的一个核心特点:

The only way to go fast, is to go well.

要想跑得快,先要跑得稳。

And that’s the answer to the executive’s dilemma. The only way to reverse the decline in productivity and the increase in cost is to get the developers to stop thinking like the overconfident Hare and start taking responsibility for the mess that they’ve made.

综上所述,管理层扭转局面的唯一选择就是扭转开发者的观念,让他们从过度自信的兔子模式转变回来,为自己构建的乱麻系统负起责任来。

The developers may think that the answer is to start over from scratch and redesign the whole system—but that’s just the Hare talking again. The same overconfidence that led to the mess is now telling them that they can build it better if only they can start the race over. The reality is less rosy:

当然,某些软件研发工程师可能会认为挽救一个系统的唯一办法是抛弃现有系统,设计一个全新的系统来替代。但是这里仍然没有逃离过度自信。试问:如果是工程师的过度自信导致了目前的一团乱麻,开始,结果就会更好呢?

Their overconfidence will drive the redesign into the same mess as the original project.

那么,我们有什么理由认为让他们从头第 1 章设计与架构究竟是什么过度自信只会使得重构设计陷入和原项目一样的困局中。

CONCLUSION 本章小结
In every case, the best option is for the development organization to recognize and avoid its own overconfidence and to start taking the quality of its software architecture seriously.

不管怎么看,研发团队最好的选择是清晰地认识并避开工程师们过度自信的特点,开始认真地对待自己的代码架构,对其质量负责。

To take software architecture seriously, you need to know what good software architecture is. To build a system with a design and an architecture that minimize effort and maximize productivity, you need to know which attributes of system architecture lead to that end.

要想提高自己软件架构的质量,就需要先知道什么是优秀的软件架构。而为了在系统构建过程中采用好的设计和架构以便减少构建成本,提高生产力,又需要先了解系统架构的各种属性与成本和生产力的关系。

That’s what this book is about. It describes what good clean architectures and designs look like, so that software developers can build systems that will have long profitable lifetimes.

这就是这本书的主题。本书为读者描述了什么是优秀的、整洁的软件架构与设计,读者可以参考这些设计来构建一个长期稳定的、持久优秀的系统。

01
3

My professional career as a software developer began in the 1990s, at a time when the dinosaurs of Big Architecture ruled the world. To get ahead, you had to learn about objects and components, about design patterns, and about the Unified Modeling Language (and its precursors).

我的软件工程师生涯开始于 20 世纪 90 年代,那是一个恐龙级大型架构统治世界的时代。要想在那样的时代获得一席之地,我们必须学会对象及其组件、设计模式、统一建模语言(包括其前身)的相关知识。

Projects—and boy, should we rue the day when we decided to call them that?—started with long design phases, where detailed blueprints for our systems were laid out by “senior” programmers for more “junior” programmers to follow. Which, of course, they didn’t. Ever.

现在想起来,或许真的可以考虑把我们那段日子所做的事情叫作“童子军项目”。每个项目的开头都会有一段长长的设计阶段,以便等那些“高级”程序员为一些跟随他们的、较“低级”的程序员制订好系统的设计蓝图,当然,这些“高级”程序员似乎永远完不成这件事。

And so it was that, after rising to the lofty ranks of “software architect”—and then “lead architect,” “chief architect,” “Lord Architect of the Privy Council,” and all the other highfalutin titles we gave ourselves back in the day—I seemed doomed to spend my days connecting boxes with arrows and coding with PowerPoint, and having barely any impact on the real code itself.

于是乎,做这件事的人被升级到了“软件架构师”,接着是“首席架构师”“总架构师”“枢密院首席架构师”以及其他各种高不可言的头衔,最终,我们还是让一切回到了原点。而我似乎注定要把时间花在画那些带箭头的盒子和编写 PowerPoint 的事情上,而这些事对真实代码的影响近乎为零。

It struck me then that this was nonsense; every line of code contains at least one design decision, and therefore anyone who writes code has a much greater impact on the quality of the software than a PowerPoint jockey like me ever could.

这让我无比受挫,每一行代码本身都至少包含了一条设计决策,任何一个写代码的家伙对软件质量的影响都远在我这个 PowerPoint 专业户之上。

And then, thankfully, the Agile Software Development revolution arrived and put architects like me out of our misery. I’m a programmer. I like programming. And the best way I’ve found to have a positive impact on code is to write it.

幸运的是,接下来发生的敏捷软件开发革命终于让我们这些架构师脱离了苦海。毕竟我是一名程序员,喜欢的是编程。而且我也发现影响软件质量最好的方法还是后序编写代码。

The dinosaurs of Big Architecture—typically to be found wandering the primeval plains of Big Process—were wiped out by the asteroid of Extreme Programming. And it came as a blessed relief.

这些大型架构像恐龙一样在大进程式的原始平原上游荡,然后被一颗叫作“敏捷开发”的小行星灭绝了,真是老天开眼啊!

Development teams were set free to focus on what matters and to concentrate their efforts on things that add value. Instead of waiting weeks or months for a Big Architecture document so they could dutifully ignore it and write the code they were going to write anyway, teams could just agree to a test with their customer, have a quick design session to get their bearings, and then write the code they were going to write anyway.

现在!开发团队可以自由地专注于真正重要的内容,并思考如何为他们所做的事情添加更多价值了。也就是说,他们现在再也不需要浪费几周或几个月的时间等待那些大型架构的设计文档了,他们可以名正言顺地忽略这些设计,直接按照自己的想法编写代码。然后,开发团队只需要安排客户直接参与测试,并在快速设计会议上得到用户的支持,然后他们就可以继续写代码了。

The Big Architecture dinosaurs were gone, and small, nimble Just-Enough-Design-Up-Front-with-Plenty-of-Refactoring mammals replaced us. Software architecture became responsive.

大型架构像恐龙一样消失了,前期设计够用、后期进行大量重构的设计思想如小巧玲珑的哺乳动物一样代替了它们,软件架构迎来了响应式设计的时代。

Well, that was the theory, anyway.

好吧,无论如何,以上这些都属于理论。

The problem with leaving architecture to programmers is that programmers have to be able to think like architects. It turns out that not all of the stuff we learned during the Big Architecture era was of no value. The way that software is structured can have a profound impact on our ability to keep adapting and evolving it, even in the short term.

把架构设计工作交给程序员的问题就是,程序员必须学会像架构师一样思考问题。事实证明,我们在大型架构时代学到的东西也并非一文不值。其设计软件结构的方法依然在我们保持软件的适应和扩展能力方面有着深远的影响,即使在短期开发中也是如此。

Every design decision needs to leave the door open for future changes. Like playing pool, each shot isn’t just about sinking that ball; it’s also about lining up the next shot. Writing working code that doesn’t block future code is a non-trivial skillset. It takes years to master.

我们的每一项设计决策都必须为未来的变化敞开大门。就像打台球一样,我们的每一杆击球都不只是为了要把球打进洞里,它也事关下一杆击球时所在的位置。让我们现在编写的代码不对未来的代码产生阻碍是一项非常重要的技能,通常需要花费多年的时间才能掌握。

And so, the era of Big Architecture gave way to a new era of Fragile Architecture: designs that grew quickly to deliver value sooner, but that made sustaining that pace of innovation very difficult.

因此,在大型架构时代让位给易碎型架构(Fragile Architecture)的新时代之后,虽然设计创造的价值得到了快速发展,但这也让我们想要持续创新变得举步维艰。

It’s all very well talking about “embracing change,” but if it costs $500 to change a line of code, change ain’t happening.

这里所有关于“拥抱变革"的讨论都很美好,但如果每修改一行代码的代价是 500 美元的话,这些变革恐怕根本就不会发生。

Bob Martin’s original papers on OO design principles had a big impact on me as a young software developer. I looked at my code with a fresh perspective, and noticed problems that—until then—never seemed like problems to me.

当我还是一名年轻的软件开发者的时候,Bob Martin 那篇关于面向对象设计原则的论文对我产生了很大的影响,他让我以一种全新的视觉审视了自己的代码,并发现了其中的问题,在那之前,这些问题对我来说似乎从来都不是问题。

Now you’ve seen how it’s possible to write code that delivers value today without blocking future value tomorrow; the onus is on you to put in the practice so you can apply these principles to your own code.

现在,你们也看到了如何才能写出既能提供当前价值,又不会阻碍未来价值的代码,期待你们也能亲自实践这些设计原则,并将其应用到自己的代码中。

Like riding a bicycle, you can’t master software design just by reading about it. To get the best from a book like this, you need to get practical. Analyze your code and look for the kinds of problems Bob highlights, then practice refactoring the code to fix these problems. If you’re new to the refactoring discipline, then this will be a doubly valuable experience.

就像学习骑自行车一样,单纯靠阅读是无法掌握软件设计方法的。为了让我们从这本书中的获益最大化,亲自实践是必不对少的。我们需要亲自分析自己的代码,查看其中是否存在 Bob 所强调的各种问题,然后在重构代码的实践中修复它们。如果你在重构方面是个新手,那么你将从本书收获双重的宝贵学习经验。

Learn how you can incorporate design principles and Clean Architecture into your development processes, so that new code is less likely to cause pain. For example, if you’re doing TDD, make a point of having a little design review after passing each test, and clean up as you go. (It’s way cheaper than fixing bad designs later.) Perhaps, before you commit code, ask a colleague to review it with you. And look into the possibility of adding a code “quality gate” to your build pipeline as a last line of defense against unclean architecture. (And if you don’t have a build pipeline, maybe it’s time to create one?)

我们得学会将书中的这些设计原则以及整洁架构融入自己的开发过程中,这可以大大减少新代码给我们带来的麻烦。例如,如果我们现在正在进行一次测试驱动的开发(TDD),就可以在每一次测试之后做一些设计审查,并及时整理我们的设计(这比事后再修复这些不良设计要省时省力得多)。或者,在提交代码之前,我们也可以邀请同事一起审查代码。另外,我们也可以研究在构建软件的管道中引入一些代码的“质量把关”机制,以作为防止架构设计不够清晰分明的最后一道防线。(如果你还没有设置构建软件的管道,现在是否可以考虑设置一个了?)

Most important of all is to talk about Clean Architecture. Talk about it with your team. Talk about it with the wider developer community. Quality is everybody’s business, and it’s important to reach a consensus about the difference between good and bad architecture.

这一切的重中之重就是要讨论架构的整洁性,我们要在自己的团队中讨论它,在各种开发者社区中讨论它。保证软件质量是我们每个人的责任,在区分架构的好坏标准上达成共识是一件非常重要的事。

Be mindful that most software developers are not very architecture-aware, just as I wasn’t 25 years ago. More experienced developers clued me into it. Once you’ve wrapped your head around Clean Architecture, take the time to wrap someone else’s head around it. Pay it forward.

我们必须意识到,大部分的软件开发者是没有太多架构意识的。就像 25 年前的我一样,是更有经验的开发者让我了解了架构。一旦我们一头扎进了整洁架构中,就会花时间围绕着它思考问题,并玩转它。

While the technology landscape for developers evolves continuously, foundational principles like the ones described here rarely change. I have little doubt that this is a book that’s going to stay on your shelf for many years after your copy of Lean JSON Cloud NoSQL for Dummies has ended up in a yard sale. I hope it does for your Design Fu what Bob’s original papers did for mine.

虽然开发者所在的技术环境一直在不断地发展,但本书所讨论的这些基本设计原则几乎不会发生变化。我一点都不怀疑在你们把 Lean JSON Cloud NoSQL for Dummies 当废纸卖掉很多年之后,这本书会还留在你们的书架上。我希望这本书会 对你们有很大的帮助,就像 Bob 那篇原创论文对我的帮助一样。

The real journey starts here.

愿你们真正的编程设计之旅从这里开始!

—Jason Gorman

January 26, 2017

——Jason Gorman

2017 年 1 月 26 日

10
1

若改造项目,也因历史遗留问题,数据库表设计也可能存在不合理,此时从头开始再搭建如此庞大的架子,感觉会有点虚空,同时也要考虑团队内部情况,不是那么容易上手,反而可能会违背初心,花更多时间和精力在各种模型理解上

我们完全可以为后续做铺垫,先搭建出底层基本设施,再基于此做灵活扩展即可,每个公司项目具体情况都不一样,比如仓储模式可能需要结合项目进行对应 改造,仓储只是提供了一种基本思想,若真将网上普遍流传的模式照搬可能并不是那么好用,可能会认为仓储莫不是一种反模式

.NET Core模块化插件

.NET Core内置提供了AssemblyLoadContext加载dll插件方式实现模块化,然后将其进行注册

        var mvcBuilder = services.AddMvc();
        foreach (var module in modules)
        {
            // Register controller from modules
            mvcBuilder.AddApplicationPart(module.Assembly);
        }

这种方式虽可行,在我看来只能作为一种临时解决方案并不利于长期,因为需额外创建一个新的项目,然后加载所生成dll,由于没有底层设施做支撑,所以极易引起版本不一致问题,而且手动被迫性质太强,实现模块化方案最终的目标则只需关注业务逻辑实现,我们来看看OrchardCore如何实现模块化。

OrchardCore模块化思想

这里我们并不讨论和ABP vNext二者谁更强大,没有任何意义,比如需结合现有项目情况、项目大小、是否为多租户、实施难度等等多方面考虑才能得出基本结论,而不是一味追求当前主流

比如我们只是想实现模块化方案,建议选择OrchardCore来实施,因为很简单,我们可将其剥离为我所用,而后结合项目情况是否考虑利用ABP vNext来进行分层处理。借鉴核心思想、才能保证一切可在控制范围内

首先我们先从整体上对OrchardCore做个认识,细枝末节暂不考虑:基于ASP.NET Core多租户模块化应用框架。

版本管理:无论是底层设施、基本框架、模块都通过包管理,同时框架和包版本基本(包管理走框架包版本)可以统一管理(对于版本升级很重要)

核心思想:模块实现模块特性,通过MSBuild构建主程序所添加实现模块特性的模块包,底层设施扫描模块特性将其注册到容器中,当然模块和模块特性都可进行基本信息描述

OrchardCore模块化原理

整个项目架构如下图所示

file

OrchardCore:底层设施以及可能需要添加的组件(比如本地化、日志、文件存储、缓存、Lucene等)

OrchardCore.Frameworks:MVC框架

OrchardCore.Modules:模块化包(比如邮件服务、后台作业服务、第三方集成等等)

OrchardCore.Modules.Cms:Cms模块包

OrchardCore.Themes:主题管理

OrchardCore.Cms.Web:主程序

我以内置所提供示例程序给大家讲解整个详细流程,而后有需要更细致了解的童鞋就可以很快上手了,如下示例主程序加载示例模块,主程序直接采用引用该示例模块(实际则是通过nuget下载该模块)

file

正常情况下我们通过nuget直接下载的是程序包,而OrchardCore对于入口则是利用MSBuild加载targets文件(其他组件则直接下载对应包),而targets引用对应包,通过这种中转方式根据我的理解主要解决了两个问题,其一则是可以屏蔽底层设施包(一次性下载),最重要的是通过targets文件可自动添加主程序程序集所加载模块包特性

是不是感觉有点懵,那到底是如何加载模块包特性的呢?来,请看如下图,我们以实际操作从头再来做一个完整梳理(注意:为排版美观,如下都将省略OrchardCore前缀)

file

【1】创建Mvc.Web程序,在nuget上下载Application.Mvc.Targets包

【2】创建Mvc.HelloWorld模块,在nuget上下载引用Module.Targets包

【3】Mvc.Web主程序引用我们所使用的Mvc.HelloWorld模块

【4】Application.Mvc.Targets包引用Application.Targets(引入底层设施)和MVC.Core(引入MVC框架)

【5】示例模块引入模块包,该包中存在模块特性(Module类)

【6】Application.Targets包下存在Application.Targets.targets文件,由于主程序引用了该包,添加所引用实现模块特性的包程序集信息到主程序集

到这里我们已经研究完主程序如何识别模块包,接下来则是如何加载模块包以及对应注册服务信息

OrcharCore核心在于OrchardCore和OrchardCore.Abstractions这两个底层设施包

归根到底,其底层设施源码一部分可能从官方源码拷贝过来(自我猜测),为实现多租户模式,势必要构建租户的容器和路由中间件,这中间就涉及在容器中需要维护每一个租户上下文(ShellContext),并且也要跟踪每个租户的状态。

ModularTenantContainerMiddleware作为创建租户容器中间件

ModularTenantRouterMiddleware作为租户路由中间件

30
6

在本指南中,您将使用项目模板将Orchard Core设置为内容管理系统。

您需要什么

.NET SDK的当前版本。您可以从以下网址下载它:https://dotnet.microsoft.com/download.
文本编辑器和终端,您可以在其中键入 dotnet 命令。

创建项目

有多种创建Orchard Core网站和模块的方法。您可以在这里了解更多信息。

在本指南中,我们将使用“代码生成模板”。您可以使用此命令安装最新稳定版本的模板:

dotnet new install OrchardCore.ProjectTemplates::1.5.0-*

注意

要使用模板的开发分支,请添加 --nuget-source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json

创建一个空文件夹来存放你的网站。打开终端,进入该文件夹并运行以下命令:

dotnet new occms -n MySite

这样就在一个名为 MySite 的文件夹中创建了一个新的Orchard Core CMS项目。

设置站点

应用程序已经由模板创建,但尚未设置。

通过执行以下命令启动应用程序:

dotnet run --project .\MySite\MySite.csproj

Note

如果你正在使用模板的开发分支,请在运行应用程序之前运行dotnet restore .\MySite\MySite.csproj --source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json

现在您的应用程序应该正在运行,并监听以下端口: 现在监听 on: https://localhost:5001 和 on: http://localhost:5000。应用已经启动,按 Ctrl+C 可以关闭应用。

在浏览器中打开 https://localhost:5001 可以显示设置屏幕。

为了演示的目的,我们将使用 Blog 配方创建网站。Blog 配方是 Orchard Core 的 入门配方之一,其中包含一系列功能和配置 Orchard Core 网站的步骤。

完成设置表单,选择 Blog 配方和 SQLite 数据库。

提交表单后,几秒钟后您将可以看到一个博客站点。

为了配置并开始编写内容,您可以转到 https://localhost:5001/admin

概要

您刚刚创建了一个由Orchard Core CMS驱动的博客引擎。

30
6

你将要构建什么

您将创建一个模块化的 ASP.NET Core MVC 网络应用程序,类似于包含在 Orchard Core 中的 "Hello World" 应用程序示例。它包括一个网络应用程序和一个模块。网络应用程序提供了布局,而模块注册了路由并响应首页请求。您可以参考 Orchard Core 中的以下项目以获取更多信息。

src/OrchardCore.Mvc.Web
src/OrchardCore.Modules/OrchardCore.Mvc.HelloWorld
所需材料
当前版本的 .NET SDK。您可以从此处下载: https://dotnet.microsoft.com/download.
一个文本编辑器和一个终端,您可以在其中运行 dotnet CLI 命令。
创建 Orchard Core 网站和模块
有不同的方法可以为 Orchard Core 创建网站和模块。您可以在 此处 了解更多信息。

在本指南中,我们将使用我们的代码生成模板。您可以使用以下命令安装模板的最新稳定版本: dotnet new install OrchardCore.ProjectTemplates::1.5.0-*

Note

如果想使用模板的开发分支,请添加 --nuget-source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json

创建一个名为OrchardCore.Mvc的空文件夹,它将包含我们的项目。打开一个终端,进入该文件夹并运行以下命令来创建Web应用程序:

dotnet new ocmvc -n OrchardCore.Mvc.Web

接下来,创建“ Hello World”模块。

dotnet new ocmodulemvc -n OrchardCore.Mvc.HelloWorld

将Web应用程序中指向该模块的项目引用添加到其中。

dotnet add OrchardCore.Mvc.Web reference OrchardCore.Mvc.HelloWorld

可选地,您可以添加一个解决方案文件,该文件引用了Web应用程序和模块,以便在Visual Studio中打开解决方案。

dotnet new sln -n OrchardCore.Mvc
dotnet sln add OrchardCore.Mvc.Web\OrchardCore.Mvc.Web.csproj
dotnet sln add OrchardCore.Mvc.HelloWorld\OrchardCore.Mvc.HelloWorld.csproj
测试生成的应用程序
从包含两个项目的OrchardCore.Mvc根文件夹中运行以下命令启动Web应用程序:

dotnet run --project .\OrchardCore.Mvc.Web\OrchardCore.Mvc.Web.csproj

注意

如果您正在使用模板的开发分支,请在运行应用程序之前运行dotnet restore .\MySite\MySite.csproj source https://nuget.cloudsmith.io/orchardcore/preview/v3/index.json

现在,您的应用程序应该在以下端口上运行和侦听:

现在侦听:https://localhost:5001
现在在http://localhost:5000上进行监听 应用程序已启动。按Ctrl+C关闭。
打开浏览器并导航至https://localhost:5001/OrchardCore.Mvc.HelloWorld/Home/Index。它应该会显示Hello from OrchardCore.Mvc.HelloWorld

布局来自主Web应用程序项目,而控制器、操作和视图来自模块项目。

注册自定义路由

默认情况下,模块中的所有路由都遵循模式{area}/{controller}/{action},其中 {area} 是模块名称。我们将更改模块中视图的路由以响应主页请求。

OrchardCore.Mvc.HelloWorldStartup.cs文件中,在Configure()方法中添加一个自定义路由。


    routes.MapAreaControllerRoute(
        name:“Home”,
        areaName:“OrchardCore.Mvc.HelloWorld”,
        pattern:“”,
        defaults:new {controller =“Home”,action =“Index”}
在Orchard Core中创建模块应用程序¶
在Orchard Core中,一个模块是指封装在自己的项目中的功能区块。这些模块可以通过应用程序进行加载和卸载,可以根据需要启用或禁用。在本教程中,您将学习如何创建一个用于 Orchard Core 的 ASP.NET Core MVC 模块应用程序。

先决条件

.NET Core SDK 3.1或更高版本。
Visual Studio 2019或更高版本。
步骤

1. 创建一个新的网站

打开 Visual Studio 并选择 创建新项目。
在左侧选择 ASP.NET Core Web 应用程序。
在右侧选择 Web 应用程序。
输入项目名称和位置,然后单击 创建。

2. 安装 Orchard Core

打开命令提示符。
执行以下命令:
dotnet new -i OrchardCore.ProjectTemplates::1.0.0-*
创建一个名为 OrchardCore.Web 的新文件夹,并在其中运行以下命令:
dotnet new ocmvc -n YourModuleName
完成后,切换到新的文件夹,然后运行以下命令启动应用程序:
dotnet run
导航到 https://localhost:5001 来查看网站。

3. 创建一个模块

在新文件夹的根目录中,创建一个名为 Modules 的文件夹。
在 Modules 文件夹中创建一个名为 YourModuleName 的新文件夹。
在 YourModuleName 文件夹中,创建一个新的 .NET Core 类库 项目。
选择 netstandard2.0 作为目标框架。
将生成的默认文件删除。
打开 YourModuleName.csproj 文件并添加以下 Orchard Core引用:

  
  

右键单击项目并选择 添加 -> 新项目文件夹。该目标文件夹应该是你的 Views 因此输入 Views 作为目录名称。 添加 Home 目录和 Index.cshtml 视图文件。 打开 Startup.cs 文件并将以下代码添加到 ConfigureServices* 方法的末尾:

services.AddMvc().AddOrchardCoreMvc();
这将向依赖注入容器注册OrchardCore MVC的必要服务。 现在,要将新的模块添加到 Orchard Core应用程序中,请打开 OrchardCore.Web.csproj* 文件并添加以下行:


  

重启应用程序,并导航到 https://localhost:5001 将显示 Orchard Core的默认欢迎页面。

4. 更改默认欢迎页面

打开 YourModuleName 项目的 Controllers 文件夹。
创建一个名为 HomeController.cs 的新文件并输入以下内容:
using Microsoft.AspNetCore.Mvc;

namespace YourModuleName.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      return View();
    }
  }
}
这将创建一个名为 Index 的动作,该动作在呈现视图时显示。 * 将以下代码复制到您的视图文件(Views/Home/Index.cshtml)中以显示 Hello World:

Hello World

重启应用程序并导航到 https://localhost:5001 将显示 Hello World 消息。 总结 您刚刚创建了一个使用Orchard Core的模块化ASP.NET Core MVC web应用程序。 它包括一个提供布局的Web应用程序和一个响应主页请求的自定义模块。 教程 https://www.youtube.com/watch?v=LoPlECp31Oo
20
6

OrchardCore入门指南

0
归档:2023年6月分类:C#和.NET

指南
无论你在构建什么,这些指南旨在让你尽快使用最新的Orchard Core项目版本和Orchard团队推荐的技术,进入工作状态。

入门指南
这些指南旨在在15-30分钟内完成,提供快速的、实践操作的指令,用于使用Orchard Core构建任何开发任务的“Hello World”。在大多数情况下,唯一的先决条件是一个.NET SDK和一个文本编辑器。

创建一个模块化的ASP.NET Core应用程序
在启动时运行代码
自定义编码设置
Orchard Core CMS指南
这些指南专门针对Orchard Core CMS:

创建Orchard Core CMS网站
向管理导航添加菜单项
安装本地化文件
如何使用资产转码器/绑定器/缩小器管道- 集成 Facebook 插件
实现全文检索
将 AzureAD 集成为外部提供程序
教程
这些教程旨在在2-3小时内完成,提供更深入、上下文探讨企业应用程序开发主题,并让您准备实现现实世界的解决方案。

使用 Razor Pages 构建解耦的网站
构建一个来自 Web 模板的网站(待定)
实施自助式 SaaS 解决方案(待定)

07
6

基于ASP.NET Core的整洁架构

0
归档:2023年6月分类:LAMP开发

干净体系结构将业务逻辑和应用程序模型置于应用程序的中心。 而不是让业务逻辑依赖于数据访问或其他基础设施,此依赖关系被倒置:基础结构和实现细节依赖于应用程序内核。 此功能是通过在应用程序核心中定义抽象或接口来实现的,然后通过基础设施层中定义的类型实现。 将此体系结构可视化的常用方法是使用一系列同心圆,类似于洋葱。

遵循依赖倒置原则以及域驱动设计原则 (DDD) 的应用程序倾向于达到类似的体系结构。 多年来,这种体系结构有多种名称。 最初的名称之一是六边形体系结构,然后是端口 - 适配器。 最近,它被称为洋葱体系结构或干净体系结构。 此电子书中将后一种名称“干净体系结构”用作此体系结构的名称。

在此关系图中,依赖关系流向最里面的圆。 “应用程序内核”因其位于此关系图的核心位置而得名。 从关系图上可见,该应用程序内核在其他应用程序层上没有任何依赖项。 应用程序的实体和接口位于正中心。 在外圈但仍在应用程序核心中的是域服务,它通常实现内圈中定义的接口。 在应用程序内核外面,UI 和基础结构层均依赖于应用程序内核,但不一定彼此依赖。

08
5

最近创建基于firstcode的开发模式,整理了一下如何使用Entity Framework Core 创建DbContext。

1、第一种重写DbContext的OnConfiguring方法,每次生成一个DbContext的方法的时候就会重新来这个方法这里读一下配置。

这种情况下,首先尝试通过调用 Program.CreateHostBuilder()、调用 Build(),然后访问 Services 属性来获取服务提供程序。

public class Program
{
    public static void Main(string[] args)
        => CreateHostBuilder(args).Build().Run();

    // EF Core uses this method at design time to access the DbContext
    public static IHostBuilder CreateHostBuilder(string[] args)
        => Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(
                webBuilder => webBuilder.UseStartup<Startup>());
}

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
        => services.AddDbContext<ApplicationDbContext>();

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    }
}

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

2、使用不带参数的构造函数

这种情况就是在已经注入的情况下,就不再需要自己去New一个对象了,直接在使用的地方把DbContext 当成构造参数传进来

如果无法从应用程序服务提供程序获得 DbContext,则这些工具将查找项目中的派生 DbContext 类型。 然后,它们尝试使用不带参数的构造函数创建实例。 如果 DbContext 是使用 OnConfiguring 方法配置的,则这可以是默认构造函数。

从设计时工厂还可以通过实现 Microsoft.EntityFrameworkCore.Design.IDesignTimeDbContextFactory 接口来告知工具如何创建 DbContext:如果在与派生的 DbContext 相同的项目中或在应用程序的启动项目中找到实现此接口的类,则这些工具会绕过创建 DbContext 的其他方式,转而使用设计时工厂。

public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
    public BloggingContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
        optionsBuilder.UseSqlite("Data Source=blog.db");

        return new BloggingContext(optionsBuilder.Options);
    }
}

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。