typescript_清潔架構的TypeScript刺

typescript

by Warren Bell

沃倫·貝爾(Warren Bell)

清潔架構的TypeScript刺 (A TypeScript Stab at Clean Architecture)

剝洋蔥皮的方法不止一種。 (More than one way to skin an onion.)

清潔建筑 (Clean Architecture)

There are many videos and articles explaining clean architecture. Most of these go over the concepts from a 20,000 foot view. I don’t know about you, but I don’t learn things very well at that elevation. Not a lot of oxygen up there. I learn by jumping in head first and coding. This article and the accompanying code is what I ended up with after such a leap.

有許多視頻和文章介紹了干凈的體系結構。 其中大多數從20,000英尺的角度審視了這些概念。 我不了解你,但是我在那個高度上學得不好。 那里沒有很多氧氣。 我通過跳入頭腦和編碼來學習。 經過如此飛躍,本文和附帶的代碼才是我最終的目標。

鮑伯的叔叔 (Bob’s Your Uncle)

The term “Clean Architecture” was made popular by Robert Martin (Uncle Bob) and his book “Clean Architecture: A Craftsman’s Guide to Software Structure and Design.” Now I don’t proclaim to be an expert in this field and I haven’t read his book, though I intend to. But I can completely relate to the problems it is trying to solve.

Robert Martin(鮑勃叔叔)和他的書《 Clean Architecture:軟件結構和設計的工匠指南》使“ Clean Architecture”一詞成為流行。 現在,盡管我打算,但我并沒有宣布自己是該領域的專家,也沒有讀過他的書。 但是我完全可以解決它要解決的問題。

How do you write a software system that is not dependent on anything other than a primary language ? We were promised this in the past with interfaces and other OO principles, but I had never before seen a “clean”, pun intended, explanation on how to do this regarding the whole system. And yes, I am a bit late to this party, being that Uncle Bob started to talk about these concepts in 2012, which is a century ago in software years.

您如何編寫不依賴于主要語言之外的任何東西的軟件系統? 過去,我們曾通過接口和其他OO原則向我們承諾過這一點,但是我從來沒有見過關于如何在整個系統上做到這一點的“干凈的”雙關語。 是的,我參加這個聚會有點晚了,因為Bob叔叔在2012年(這是一個世紀的軟件時代)開始談論這些概念。

令我困惑的圖 (The Diagram That Baffled Me)

Here is the original diagram Uncle Bob and others used in their presentations when explaining Clean Architecture. This simple little diagram became an obsession of mine. I had long ago purged my memory of anything UML related and was struggling with the has-a, and uses-a relationships indicated by the open and close arrow heads. The only way I was going to figure this out was by writing some code.

這是Bob叔叔和其他人在解釋Clean Architecture時在演示中使用的原始圖。 這個簡單的小圖成了我的癡迷。 我很早以前就清除了與UML相關的任何內容,并且一直在用打開和關閉箭頭指示的has-a和use-a關系進行掙扎。 我要弄清楚這一點的唯一方法是編寫一些代碼。

認識你的洋蔥 (Know Your Onions)

One way to look at Clean Architecture is as an onion with layers. All layers can only depend on a layer that is closer to the center. That is, all dependencies point inward and not outward.

觀察“干凈架構”的一種方法是將洋蔥分層。 所有圖層只能依賴于更靠近中心的圖層。 也就是說,所有依賴關系都指向內部而不是向外。

這些日子之一,我要組織起來 (One of These Days, I’m Gonna Get Organizized)

In our example, there are 4 modules that correspond with each layer of this onion. Eventually these could be separate npm modules. For readability’s sake, I tried to name things according to Uncle Bob’s original Clean Architecture diagram at the top of this article. In the real world you would probably preface all names with what ever use case they represented.

在我們的示例中,有4個模塊與該洋蔥的每一層相對應。 最終,這些可以是單獨的npm模塊。 出于可讀性考慮,我嘗試根據本文頂部Bob叔叔的原始Clean Architecture圖來命名。 在現實世界中,您可能會在所有名稱的開頭加上它們所代表的用例。

The infrastructure (blue) layer is where all of our outside pluggable systems live. These outside systems such as devices, web, and UIs, shown in the onion diagram, will use our IRequest and IViewModel interfaces to communicate with our Controller and Presenter while the db and external interfaces, shown in the onion diagram, will use the IEntityGateway interface to communicate with our Interactor.

基礎架構(藍色)層是我們所有外部可插拔系統所在的位置。 洋蔥圖中顯示的這些外部系統,例如設備,Web和UI,將使用IRequest和IViewModel接口與Controller和Presenter進行通信,而洋蔥圖中顯示的db和外部接口將使用IEntityGateway接口與我們的交互器進行通信。

Our example will have one entity called a Widget with three properties. It also uses one use case “create widget” which takes a widget from the UI, saves the widget to some sort of storage, and returns back to the UI a newly created widget with an id and a revision number.

我們的示例將具有一個名為Widget的實體,該實體具有三個屬性。 它還使用了一個用例“創建窗口小部件”,它從UI中獲取窗口小部件,將窗口小部件保存到某種存儲中,并將具有ID和修訂號的新創建的窗口小部件返回給UI。

更多視覺輔助 (More Visual Aids)

Here is the directory structure. Everything gets wired together in each module’s index.ts file. The entry point is demonstrated in a test located in infrastructure/test/TestEntryPoint.spec.ts .

這是目錄結構。 一切都在每個模塊的index.ts文件中連接在一起。 入口點在位于Infrastructure / test / TestEntryPoint.spec.ts中的測試中進行了演示。

他去了哪條路? (Which Way Did He Go?)

One of my original hangups was how the outer most layer communicates with the inner layers. I thought all you had to do is call some createWidget() function, for example, on a Controller and you would get a nice shiny new widget returned back to you. Wrong.

我最初的掛斷之一是最外層如何與內層進行通信。 我以為您要做的就是在控制器上調用一些createWidget()函數,您會得到一個漂亮的閃亮新小部件,該小部件將返回給您。 錯誤。

What you want to do is send the widget to be created down to the use case (Interactor) on a certain path and have the use case (Interactor) send the new widget back up to you on a different path. This is similar to a callback function or a Promise. I found a good diagram illustrating this in an article titled “Implementing Clean Architecture — Of controllers and presenters” (link below). In our example I have not implemented a RequestModel or a ResponseModel.

您想要做的是將要創建的小部件發送到特定路徑上的用例(Interactor),并讓用例(Interactor)在另一條路徑上將新的小部件發送回給您。 這類似于回調函數或Promise。 我在題為“實現控制器和演示者的清潔架構”的文章中找到了說明這一點的好圖表(下面的鏈接)。 在我們的示例中,我尚未實現RequestModel或ResponseModel。

進入,跨過和跳出OO方法 (Step Into, Step Over, and Step Out, the OO Way)

So let’s first create a widget with classes and interfaces.

因此,讓我們首先創建一個帶有類和接口的小部件。

面向對象的步驟1 (OO Step 1)

This is the entry point. This code would be located in the infrastructure (blue) layer. This layer is where your mobile app, web app, API, CLI, etc. lives. Also all of your outside systems like external APIs, frameworks, libraries and databases live here too. Everything is pluggable in this layer and communicates with our system via interfaces we provide.

這是入口點。 此代碼將位于基礎結構(藍色)層中。 該層是您的移動應用程序,Web應用程序,API,CLI等所在的位置。 同樣,您的所有外部系統(例如外部API,框架,庫和數據庫)也都位于此處。 一切都可插入此層,并通過我們提供的接口與我們的系統通信。

First you create your ViewModel implementation of the IViewModel interface where a new widget will appear and you can update your UI within the implemented function presentWidget(widget).

首先,您將創建IViewModel接口的ViewModel實現,其中將出現一個新的小部件,并且可以在已實現的函數presentWidget(widget)中更新UI。

You then create a Controller that implements the IRequest interface by passing the EntityGateway and the ViewModel you created above to a constructor. Finally your UI calls createWidget(widget) on the Controller where your new widget begins its journey to the Interactor.

然后,通過將上面創建的EntityGateway和ViewModel傳遞給構造函數,來創建實現IRequest接口的Controller。 最后,您的UI調用Controller上的createWidget(widget),新的小部件開始進入Interactor的旅程。

什么是EntityGateway? (What’s an EntityGateway?)

An EntityGateway implements the IEntityGateway interface and is where you implement specific code that persists your widget. It lives in the infrastructure (blue) layer. This could be any type of existing or future external API or persistence system such as a database.

EntityGateway實現IEntityGateway接口,在這里您可以實現持久化窗口小部件的特定代碼。 它位于基礎結構(藍色)層中。 它可以是任何類型的現有或將來的外部API或持久性系統,例如數據庫。

To change out to a different system, you would just simply wire together the new EntityGateway implementation with the IEntityGateway interface. In this example, I use a Promise to simulate some sort of persistence operation.

要更改為其他系統,只需將新的EntityGateway實現與IEntityGateway接口連接在一起。 在此示例中,我使用Promise模擬某種持久性操作。

連線什么? (Wire up What?)

The file infrastructure/src/index.ts in the infrastructure module is where you can wire up your different implementations of your IEntityGateway interface. The “from” path of the import statement points to the correct implementation. In this case it is a persistence system named AnyDB.

基礎結構模塊中的文件Infrastructure / src / index.ts是連接IEntityGateway接口的不同實現的位置。 import語句的“ from”路徑指向正確的實現。 在這種情況下,它是一個名為AnyDB的持久性系統。

Uncle Bob also talks about the use of a Main class where you can do this type of wiring up or do other initializing code. The Main class would also live in the infrastructure module and be pluggable. It would also communicate in the same manner as the other systems in the infrastructure module do. For example, you would initiate this class in your UI’s initializing code and pass it down into your more inner layers to be used via some sort of configuration interface.

Bob叔叔還談到了Main類的使用,您可以在其中進行這種類型的連接或進行其他初始化代碼。 Main類還將存在于基礎結構模塊中并且是可插入的。 它還將以與基礎結構模塊中其他系統相同的方式進行通信。 例如,您可以在UI的初始化代碼中初始化此類,然后將其傳遞到更內部的層中,以通過某種配置界面使用。

Our example does not use a Main class and instead is passing the persistence system down into the interactor via the createWidget() function. This is probably not the “pure” way of doing this, but was done to make our example easier to read.

我們的示例不使用Main類,而是通過createWidget()函數將持久性系統向下傳遞給交互器。 這可能不是做到這一點的“純粹”方法,但是這樣做是為了使我們的示例更易于閱讀。

面向對象的步驟2 (OO Step 2)

The Controller is a very busy place. First, the EntityGateway passes through unchanged to our Interactor constructor. Then our ViewModel gets passed to the constructor of our Presenter which in turn also gets passed to our Interactor constructor. This all happens in the createWidget(widget) function of the Controller which was called by our UI in step 1 via the IRequest interface. We will talk about our Presenter in step 4 when the newly created widget travels back up to the UI.

控制器是一個非常繁忙的地方。 首先,EntityGateway不變地傳遞給我們的Interactor構造函數。 然后,我們的ViewModel被傳遞給Presenter的構造函數,后者又被傳遞給Interactor構造函數。 所有這些都發生在Controller的createWidget(widget)函數中,該函數在第1步中通過IRequest接口由我們的UI調用。 當新創建的小部件返回到UI時,我們將在步驟4中討論Presenter。

面向對象的步驟3 (OO Step 3)

Finally we are at the most inner layer of our journey, the usecase layer where our Interactor lives. Or better known as our home for all of our app’s use case logic. There is one more inner layer, the domain. This is where all of our business entities and business specific logic lives. In this example, we really don’t have any need to go there except to borrow the WidgetType and IEntityGateway interfaces.

最后,我們處于旅程的最內層,即Interactor所在的用例層。 或更廣為人知的是我們所有應用程序用例邏輯的家。 還有一層內層,即領域。 這是我們所有業務實體和業務特定邏輯的所在地。 在此示例中,除了借用WidgetType和IEntityGateway接口之外,我們真的不需要去那里。

莫文 (Movin On Up)

Here in our Interactor we take the EntityGateway that was passed through from the Controller and call its saveWidget(widget) function via the IEntityGateway interface. This function returns a Promise from the EntityGateway which resolves in .then() with a newly created widget. We then call the Presenter’s presentWidget(widget) function via the IOutputBoundary interface which starts the newly created widget back up to the UI. This all happens in the Interactor’s createWidget(widget) function which was called by our Controller via the IInputBoundary interface in step 2.

在我們的Interactor中,我們采用從Controller傳遞過來的EntityGateway,并通過IEntityGateway接口調用其saveWidget(widget)函數。 此函數從EntityGateway返回一個Promise,它使用新創建的小部件在.then()中解析。 然后,我們通過IOutputBoundary接口調用Presenter的presentWidget(widget)函數,該接口將新創建的小部件啟動回到UI。 這一切都發生在Interactor的createWidget(widget)函數中,該函數在步驟2中由我們的控制器通過IInputBoundary接口調用。

面向對象的步驟4 (OO Step 4)

Here in our Presenter, we simply pass the widget to our ViewModel’s presentWidget(widget) function we created in our UI. This all happens in the Presenter’s presentWidget(widget) function via the IOutputBoundary interface which was called in the Interactor’s createWidget(widget) function in step 3. More can happen here, but not in our example.

在Presenter中,我們只需將小部件傳遞給我們在UI中創建的ViewModel的presentWidget(widget)函數。 所有這些都通過IOutputBoundary接口在Presenter的presentWidget(widget)函數中發生,該接口在第3步中的Interactor的createWidget(widget)函數中被調用。

面向對象的步驟5 (OO Step 5)

Finally our newly created widget is back home ready to be displayed in our UI. This is the exact spot (code) where we started in step 1. Updating the UI happens in the ViewModel’s presentWidget(widget) function via the IViewModel interface which was called in the Presenter’s presentWidget(widget) function in step 4.

最后,我們新創建的窗口小部件已準備就緒,可以在我們的UI中顯示。 這是我們從步驟1開始的確切位置(代碼)。UI的更新是通過IViewModel接口在ViewModel的presentWidget(widget)函數中進行的,該接口在步驟4中在Presenter的presentWidget(widget)函數中調用。

OO支持演員 (OO Supporting Cast Members)

Here are all the remaining interfaces and type definitions clumped together in one file.

這是所有剩余的接口和類型定義,它們集中在一個文件中。

2個人進入1個人離開 (2 Men Enter 1 Man Leaves)

I wrote the class and interface version of this project first. I wanted to try and make it match Uncle Bob’s original diagram as close as I could. When I finished that project, I realized I could have done the same thing with functions and type definitions. So I created an identical project and replaced Classes with Functions and Interfaces with Type definitions.

我首先編寫了該項目的類和接口版本。 我想嘗試使其與Bob叔叔的原始圖盡可能地接近。 當我完成該項目時,我意識到我可以使用函數和類型定義完成同樣的事情。 因此,我創建了一個相同的項目,并使用函數和接口將類型替換為類型定義。

And here is the difference between a Controller class and a Controller function.

這是Controller類和Controller函數之間的區別。

進入,跨出和退出功能方式 (Step Into, Step Over, and Step Out, the Function Way)

Now lets give a stab at creating widgets with functions and type definitions.

現在,讓我們扼殺使用函數和類型定義創建小部件的過程。

一般差異 (General Differences)

WidgetType is identical as the OO version above and IEntityGateway, IRequest, IViewModel, IInputBoundary, and IOutputBoundary are now type definitions instead of interfaces.

WidgetType與上面的OO版本相同,并且IEntityGateway,IRequest,IViewModel,IInputBoundary和IOutputBoundary現在是類型定義,而不是接口。

功能步驟1 (Function Step 1)

Every thing is the same as OO step 1 above, other than that we are now importing a function named “controllerConstructor” instead of a class named “Controller.” And importing a function named “entityGateway” instead of a class named EntityGateway. Last but not least, the ViewModel we created is now an object with a presentWidget() function in it instead of a class with a presentWidget() function.

每件事都與上面的OO步驟1相同,除了我們現在要導入一個名為“ controllerConstructor”的函數,而不是一個名為“ Controller”的類。 然后導入一個名為“ entityGateway”的函數,而不是一個名為EntityGateway的類。 最后但并非最不重要的一點是,我們創建的ViewModel現在是一個帶有presentWidget()函數的對象,而不是帶有presentWidget()函數的類。

實體網關又來了? (EntityGateway Again?)

The EntityGateway does the same task as the OO version above. It is now a function instead of a class. It returns a saveWidget() function wrapped in an object.

EntityGateway與上述OO版本執行相同的任務。 現在它是一個函數而不是一個類。 它返回包裝在對象中的saveWidget()函數。

更多接線 (More Wiring)

Same as OO version above except we are now exporting a function instead of a class.

與上面的OO版本相同,除了我們現在導出的是函數而不是類。

功能步驟2 (Function Step 2)

Our Controller is still a busy place and does the same tasks as the OO version. We are now importing a function named interactorConstructor instead of a class named Interactor. We are exporting a function named “controllerConstructor” instead of a class named “Controller.” It returns a function named “createWidget wrapped in an object.

我們的控制器仍然很忙,并且執行與OO版本相同的任務。 現在,我們將導入一個名為interactorConstructor的函數,而不是一個名為Interactor的類。 我們正在導出一個名為“ controllerConstructor”的函數,而不是一個名為“ Controller”的類。 它返回一個包裝在對象中的名為“ createWidget”的函數。

功能步驟3 (Function Step 3)

Back in the Iteractor in our usecase module, we are executing the same tasks as the OO version above. We are now exporting a function named “interactorConstructor” instead of a class named “Interactor.” It returns a function named “createWidget wrapped in an object.

回到用例模塊的Iteractor中,我們正在執行與上述OO版本相同的任務。 現在,我們正在導出一個名為“ interactorConstructor”的函數,而不是一個名為“ Interactor”的類。 它返回一個包裝在對象中的名為“ createWidget”的函數。

功能步驟4 (Function Step 4)

We are now passing the newly created widget back up in our Presenter where we are executing the same tasks as the OO version above. We export a function named “presenterConstructor” instead of a class named “Presenter.” It returns a function named “presentWidget wrapped in an object.

現在,我們將新創建的小部件傳遞回Presenter中,在其中執行與上述OO版本相同的任務。 我們導出一個名為“ presenterConstructor”的函數,而不是一個名為“ Presenter”的類。 它返回一個包裝在對象中的名為“ presentWidget”的函數。

功能步驟5 (Function Step 5)

Again we have come full circle and we are back in the exact spot (code) where we started in step 1. Our UI gets updated with our newly created widget in the ViewModel’s presentWidget() function.

再次,我們繞了一圈,回到了第一步中的確切位置(代碼)。在ViewModel的presentWidget()函數中,使用新創建的小部件對UI進行了更新。

職能支持演員 (Function Supporting Cast Members)

Here are all the remaining type definitions clumped together in one file. These are our interfaces.

以下是所有剩余的類型定義,它們集中在一個文件中。 這些是我們的界面。

所有這一切都該死的小部件? (All That for a Damn Widget?)

Yes, but you also get the promise of a completely decoupled system where you can plug in different implementations of your outside (infrastructure blue layer) systems, including different types of UIs, external APIs, databases, libraries, frameworks and more.

是的,但是您也可以得到一個完全解耦的系統的希望,您可以在其中插入外部(基礎架構藍色層)系統的不同實現,包括不同類型的UI,外部API,數據庫,庫,框架等。

我們不需要Stinkin Profiler (We Don’t Need No Stinkin Profilers)

My original hunch was that the class and interface version would be slower than the function version. So I ran both projects through my advance profiling tools of typing “npm test” and pressing enter until my finger cramped up.

我最初的直覺是類和接口的版本會比函數的版本慢。 因此,我通過高級配置工具(輸入“ npm test”)并按Enter直到手指收緊,來運行兩個項目。

My first observation was that the function version was about twice as fast, WOW. Then I decided to refactor the function version to return all of the important functions wrapped in objects so I could enforce the function names. I then ran both versions through my advance profilers and they were about the same speed.

我的第一個觀察結果是該功能版本的運??行速度大約是WOW的兩倍。 然后,我決定重構函數版本,以返回包裝在對象中的所有重要函數,以便執行函數名。 然后,我通過高級分析器運行了兩個版本,它們的速度大致相同。

I have no idea why wrapping a function in an object would slow it down that much. Maybe I didn’t actually get Adobe Flash completely uninstalled from my laptop and it decided to interfere. Anyways, it would be interesting to get a more accurate measure of speed using the correct tools against the compiled JavaScript.

我不知道為什么將函數包裝在對象中會降低它的速度。 也許我實際上并沒有從筆記本電腦上完全卸載Adobe Flash,它決定進行干預。 無論如何,使用針對已編譯JavaScript的正確工具來獲得更準確的速度度量將很有趣。

拿走 (The Take Away)

The OO version has more code but may be easier to read and follow. The function version has less code but may be harder to read and follow.

OO版本具有更多代碼,但可能更易于閱讀和遵循。 該功能版本的代碼較少,但可能難以閱讀和遵循。

Personally I like the function version, being that I have done a lot of programming in Java and I am tired of writing so many classes. One of the things I like the most about TypeScript/JavaScript is the ability to use object literals. And with TypeScript type definitions, you can now apply some safety to using object literals.

就我個人而言,我喜歡函數版本,因為我已經用Java完成了許多編程工作,并且我厭倦了編寫這么多類。 我最喜歡TypeScript / JavaScript的一件事是使用對象文字的能力。 通過TypeScript類型定義,您現在可以在使用對象文字時應用一些安全措施。

Another take away is that you don’t need to rigidly conform to the clean architecture as diagrammed above to achieve a decoupled system. For example, you could just as easily have your UI communicate directly with your use case layer bypassing the delivery layer if it’s not needed. All of these layers may physically live in different places and have different ways of communicating with each other.

另一個要解決的問題是,您無需嚴格遵循上述架構,即可實現分離的系統。 例如,如果不需要,您可以輕松地讓UI繞過交付層直接與用例層進行通信。 所有這些層在物理上可能都生活在不同的地方,并且具有彼此通信的不同方式。

試試看 (Give it a Try)

Here are some of the things I intend to enforce in my next project.

這是我打算在下一個項目中執行的一些操作。

  1. Dependencies should always go one way.

    依賴關系應該始終是一種方式。
  2. Dependencies should always point from your outside systems (UI, db, etc.) to your business entities and business logic.

    依賴關系應始終從外部系統(UI,數據庫等)指向業務實體和業務邏輯。
  3. Your inner layers (delivery, use case, and business entities) need to expose interfaces for the more outer layers to use.

    您的內部層(交付,用例和業務實體)需要公開接口以供更多外部層使用。
  4. You should always start developing from the most inner layer out. Start with the business entities and logic first and test. Create the interfaces that will be used and then test these interfaces. I am guilty of working the other way around. I think we all like to start with the UI, because it immediately lets us visually see how our system will look to a user. Plus the UI is where a lot of the “cool” technologies live.

    您應該始終從最內層開始開發。 首先從業務實體和邏輯開始,然后進行測試。 創建將要使用的接口,然后測試這些接口。 我對此感到內working。 我認為我們都喜歡從UI入手,因為它可以立即使我們直觀地看到系統對用戶的外觀。 另外,UI是許多“炫酷”技術的所在地。
  5. Use TDD (Test Driven Development). Clean architecture allows you to do this much more easily. Everything is more compartmentalized and easier to mock. The implementation of the IEntityGateway above is basically a mock of a database.

    使用TDD(測試驅動開發)。 干凈的體系結構使您更輕松地執行此操作。 一切都變得更加分隔,更易于嘲笑。 上面的IEntityGateway的實現基本上是數據庫的模擬。
  6. Last but not least, be flexible. Don’t knock yourself out trying to adhere to clean architecture when that library or framework you want to use just won’t work with it. But be warned that this is probably a good indication that you will eventually have some sort of problems regarding that library or framework, especially if it wants you to extend their classes. Decoupling should be your goal.

    最后但并非最不重要的一點是要靈活。 當您要使用的庫或框架無法使用干凈的體系結構時,請不要大驚小怪。 但請注意,這很可能很好地表明您最終將對該庫或框架產生某種問題,尤其是如果它希望您擴展其類時。 去耦應該是您的目標。

但是,但是,那... (But, But, What About…)

Please ask questions and give feedback, there is no better way to learn than to get constructive criticism from your peers. And it is highly probable that I missed something somewhere.

請提出問題并提供反饋,沒有比從同行那里獲得建設性批評更好的學習方法了。 而且我很可能在某處錯過了一些東西。

資源: (Resources:)

OO版本的代碼位于: (Code for the OO version is located at:)

https://github.com/warrenbell/cleanarch-tsoo

https://github.com/warrenbell/cleanarch-tsoo

該功能版本的代碼位于: (Code for the function version is located at:)

https://github.com/warrenbell/cleanarch-tsfun

https://github.com/warrenbell/cleanarch-ts有趣

ts節點 (ts-node)

Handy little TypeScript tool.

方便的小型TypeScript工具。

https://github.com/TypeStrong/ts-node

https://github.com/TypeStrong/ts-node

鮑勃叔叔的清潔建筑 (The Clean Architecture by Uncle Bob)

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html

這本書 (The Book)

https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164

https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164

許多視頻之一 (One of Many Videos)

They are all basically the same except for the the first 5 minutes where Uncle Bob likes to muse about something loosely related and then makes a hard segue into clean architecture.

除了前5分鐘外,它們基本上都是相同的,前5分鐘Bob叔叔喜歡思考一些松散相關的內容,然后艱難地研究出干凈的建筑。

https://www.youtube.com/watch?v=Nltqi7ODZTM

https://www.youtube.com/watch?v=Nltqi7ODZTM

實施清潔架構—控制器和演示者的 (Implementing Clean Architecture — Of controllers and presenters)

https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter/

https://plainionist.github.io/Implementing-Clean-Architecture-Controller-Presenter/

清潔建筑:站在巨人的肩膀上 (Clean Architecture: Standing on the shoulders of giants)

https://herbertograca.com/2017/09/28/clean-architecture-standing-on-the-shoulders-of-giants/

https://herbertograca.com/2017/09/28/clean-architecture-standing-on-the-shoulders-of-giants/

干凈的體系結構:用例包含演示者或返回數據? (Clean Architecture: Use case containing the presenter or returning data?)

https://softwareengineering.stackexchange.com/questions/357052/clean-architecture-use-case-containing-the-presenter-or-returning-data

https://softwareengineering.stackexchange.com/questions/357052/clean-architecture-use-case- contains-the-presenter-or-returning-data

干凈的架構。 主持人的工作是什么? (Clean architecture. What are the jobs of presenter?)

https://stackoverflow.com/questions/46510550/clean-architecture-what-are-the-jobs-of-presenter

https://stackoverflow.com/questions/46510550/clean-architecture-what-are-the-jobs-of-presenter

翻譯自: https://www.freecodecamp.org/news/a-typescript-stab-at-clean-architecture-b51fbb16a304/

typescript

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393371.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393371.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393371.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

星辰小隊針對于軟件“星遇”的第二次10天沖刺——第10天

日期:2019.6.6 博客期:099 星期四 呃~確實有一部分的功能來不及實現了,針對目前已經實現的功能開始整合,用戶界面進行美化,把多于沒用的界面和代碼類刪除掉,制作本軟件的敘述性文件和相關文檔,根…

mybatis的select、insert、update、delete語句

一、select <!-- 查詢學生&#xff0c;根據id --> <select id"getStudent" parameterType"String" resultMap"studentResultMap"> SELECT ST.STUDENT_ID, ST.STUDENT_NAME, ST.STUDENT_SEX, ST.STUDENT_BIRTHDAY, ST.CLASS_I…

appium java簡單實例_Appium創建一個Note的實例

近來通過Appium&#xff0c;Robotium等幾個框架去了解移動平臺自動化測試。Appium官方實例是使用ContactManager.apk&#xff0c;而Robotium使用的是SDK自帶的Notepad.apk&#xff0c;為了方便比較&#xff0c;在了解Appium的同時把實例修改成跟Robotium一致的Notepad.apk并記錄…

Lync Server 2013無法共享PPT故障排錯處理

前段時間幫助朋友看了一個關于Lync Server 2013無法共享PPT的問題&#xff0c;共享PPT時報如下錯誤&#xff1a; 日志截圖如下&#xff1a; 原因如下前端服務器未關聯Ofice web Application服務器&#xff0c;關聯即可&#xff0c;如下&#xff1a; 關聯完成后&#xff0c;如下…

leetcode733. 圖像渲染(bfs)

有一幅以二維整數數組表示的圖畫&#xff0c;每一個整數表示該圖畫的像素值大小&#xff0c;數值在 0 到 65535 之間。 給你一個坐標 (sr, sc) 表示圖像渲染開始的像素值&#xff08;行 &#xff0c;列&#xff09;和一個新的顏色值 newColor&#xff0c;讓你重新上色這幅圖像…

chrome擴展程序_如何創建Chrome擴展程序

chrome擴展程序by Erika Tan譚詠麟 如何創建Chrome擴展程序 (How to create a Chrome Extension) In this article, I will be teaching you how to make a Chrome Extension of your own. I’m basing it off of lessons learned while creating TalkToMe, a Chrome Extensio…

對‘初學者應該選擇哪種編程語言’的回答——計算機達人成長之路(38)

7、PASCAL語言&#xff08;一&#xff09;一門通&#xff0c;門門通 在計算機學習問題排行版上&#xff0c;有一個問題絕對是穩居榜首&#xff0c;每次提出都能在各大論壇掀起一股頂帖風暴&#xff0c;而各大網站的每個網絡大牛&#xff0c;都會收到無數學院小弟發來弱弱的提問…

leetcode110. 平衡二叉樹(遞歸)

給定一個二叉樹&#xff0c;判斷它是否是高度平衡的二叉樹。本題中&#xff0c;一棵高度平衡二叉樹定義為&#xff1a;一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過1。示例 1:給定二叉樹 [3,9,20,null,null,15,7]3/ \9 20/ \15 7 返回 true 。代碼 /*** Defi…

spring配置文件注解方式引入的兩種方式

一、#{beanID[propertiesName]}方式 <bean id"propertyConfigurer" class"org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name"fileEncoding" value"utf-8" /><property name&…

vsc 搜索特定代碼_特定問題的通用解決方案:何時編寫代碼以及何時編寫代碼...

vsc 搜索特定代碼by Rina Artstain通過麗娜阿斯特斯坦 特定問題的通用解決方案&#xff1a;何時編寫代碼以及何時編寫代碼 (Generic solutions to specific problems: when to write some code and when to just do it) There is a traditional story that tells of a rabbi w…

java手動編譯jar包_Maven 手動添加第三方依賴包及編譯打包和java命令行編譯JAVA文件并使用jar命令打包...

一&#xff0c;實例:新建了一個Maven項目,在eclipse中通過 build path –> configure path….將依賴包添加到工程中后&#xff0c;eclipse不報錯了。但是用Maven命令 mvn clean compile 時出錯如下&#xff1a;原因是在eclipse中添加了 exteneral jar后&#xff0c;還需要在…

SQL like

確定給定的字符串是否與指定的模式匹配。模式可以包含常規字符和通配符字符。模式匹配過程中&#xff0c;常規字符必須與字符串中指定的字符完全匹配。然而&#xff0c;可使用字符串的任意片段匹配通配符。與使用 和 ! 字符串比較運算符相比&#xff0c;使用通配符可使 LIKE 運…

計劃備份mysql數據庫

1:mysql是我們使用最多的數據庫&#xff0c;如果在日常中正確的對mysql數據進行備份&#xff0c;下面我們就來做這事&#xff0c;通過腳本來實現##########################################################################################################################…

leetcode1333. 餐廳過濾器

給你一個餐館信息數組 restaurants&#xff0c;其中 restaurants[i] [idi, ratingi, veganFriendlyi, pricei, distancei]。你必須使用以下三個過濾器來過濾這些餐館信息。 其中素食者友好過濾器 veganFriendly 的值可以為 true 或者 false&#xff0c;如果為 true 就意味著你…

3.27下午

轉載于:https://www.cnblogs.com/bgd140201228/p/6628194.html

2019春季學期進度報告(十四)

課上花費時間&#xff1a;5h 課下花費時間&#xff1a;6h 學會的新內容&#xff1a;阿里云服務器的購買&#xff0c;websockt入門。 代碼量&#xff1a;200h 轉載于:https://www.cnblogs.com/Aduorisk/p/11056750.html

rxjs 怎么使用_使用RxJS Observables進行SUPER SAIYAN

rxjs 怎么使用I loved DragonBall Z as a kid, and still love it as an adult. 我從小就愛DragonBall Z&#xff0c;但從小到大仍然喜歡它。 Among the ludicrous number of transformations, the original Super Saiyan remains my favorite. 在可笑的轉換數量中&#xff0c…

java編程石頭剪刀布_java 開發的石頭,剪刀,布的游戲 demo

[java]代碼庫/** 創建一個類Game&#xff0c;石頭&#xff0c;剪刀&#xff0c;布的游戲。*/public class Game {/*** param args*/String[] s {"石頭","剪刀","布"};//獲取電腦出拳String getComputer(int i){String computerGuess s[i];retur…

JList的基本操作

1.初始化并添加元素 DefaultListModel leftListModelnew DefaultListModel(); String[] items Model.getPairs(); for (int i0; i<items.length; i) { leftListModel.add(i, items[i]); } JList leftLstnew JList(leftListModel); 2.刪除所有元素 leftListModel.remove…

請求WebApi的幾種方式

請求WebApi的幾種方式目前所了解的請求WebAPI的方式有通過后臺訪問api 和通過js 直接訪問api接口 首先介紹下通過后臺訪問api的方法&#xff0c;可以使用HttpClient的方式也可以使用WebRequest的方式 1、HttpClient的方式 &#xff08;1&#xff09;Get請求 string url "…