從微服務開始 vs 不從微服務開始

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"本文最初發表於 Medium 博客,經原作者 Chris 授權,InfoQ 中文站翻譯並分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文的題目看似自相矛盾,實則不然。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我想講兩個故事。一個是不從微服務開始,一個是從微服務開始。我認爲,通過觀察事物的兩面,我們將對微服務的實際好處有更多的瞭解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"閒話少敘,言歸正題。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"不從微服務開始"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設你正在一個大型電子商務平臺上工作。和所有電子商務平臺一樣,都有一個產品列表頁面,顯示所有的產品。同時,也有一個處理購物車和結賬流程的結賬頁面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d7\/d72d660bf7e12be838f456aae072e8c3.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/01\/01fd9da19ec2f954831709bd9abb05aa.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很久以前,這家公司的 CTO,John 讀到了關於微服務方面的一些內容。他覺得微服務是個好主意。於是,他決定創建一個微服務來管理產品列表域和購物車域。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務架構允許 John 擁有一個產品清單團隊和一個結賬團隊。他們有各自的業務目標、指標、KPI、SLA 等等。這兩個團隊都是跨職能的團隊,負責產品列表面以及端到端的結賬流程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"他們每一個人都可以隨心所欲地做他們想做的任何事。舉例來說,結賬團隊可以實施他們自己的 AB 測試,並在上面設置多個本地化和一個漂亮的節日主題。產品團隊可以專注於通過建議相關項目來優化產品列表。他們可以把精力集中在自己的領域,而不必擔心會對其他領域做出破壞性的改變,只需保持域間 API 通信的向後兼容性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,我們還可以對系統進行擴展。假設我們週五有一個大型促銷活動,客戶將會看到更多的產品和促銷活動。在需要一個大點的結賬頁面的同時,我們可能需要更大的產品列表後端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完美聽起來不錯。生活美好!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二年,CEO Jane 想要搞一個促銷活動。Jane 也從一些研究中發現,如果在登陸頁面和結賬流程中展示相關的產品,能夠顯著提高銷售額。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/9a\/9aa391ba98be96a1a1fc219d3f7477d6.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/dc\/dc76ceaf8b84b9b4928e93390da92154.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"存在着特定的定價方案、相關性(消費者購買大量 IT 相關產品在 IT 產品上會獲得更多折扣)和客戶特權等級。這些應該由產品列表團隊進行管理,因爲他們已經進行了大量關於如何顯示吸引人的產品的研究。此外,產品列表團隊也知道如何用吸引人的方式來展示產品。他們很清楚什麼顏色、字體、圖像大小和邊距能夠產生最佳的轉換率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過,改變將會很艱難。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲結賬流程和產品頁面的前端和後端都屬於不同的微服務,因此需要兩個團隊共享代碼、設計、邏輯等。能夠進行簡單的複製粘貼,我們可以創建一個共享庫,並且可以公開某種 API。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"儘管它們在技術上都是可能的,但是在這種微服務架構中執行這種類型的改變需要比單體架構更加複雜。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼如果我們需要迭代設計、數據和推廣中的所有東西呢?每次迭代的每一個改變都必須反映在結賬頁面和產品列表頁面上。假如我們做得不夠好,我們就需要把所有的工作翻倍,或者在這兩個團隊之間一次又一次地將 API 重新設計。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用微服務架構後,我們就可以在很大程度上使 "},{"type":"text","marks":[{"type":"strong"}],"text":"服務內部的任何改變變得更容易,並使跨服務的改變更加困難。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b1\/b1e34e9ba4cd177466b42a7f044fa700.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲人類並不擅長預測未來,所以我們永遠不應該從微服務開始。我們不知道 6~12 個月後會有什麼業務。不管我們怎樣拆分微服務,它都有可能是錯誤的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們永遠不要從微服務開始。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"從微服務開始"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們倒回去,看看事情的發展會有什麼不同。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這家公司的 CTO,John 讀到了關於微服務方面的內容。John 認爲這微服務是個災難性的想法。微服務在技術上所解決的所有問題都可以用單體來完成,那麼爲什麼要把分佈式部分加入到方程中呢?我們的大多數程序員都會受到分佈式計算謬誤的影響。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"John 堅持使用單體架構。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"John 有一個由 30 名開發者組成的團隊。按照這個規模,John 是無法審查他那龐大的單體的每一個改變的。因此他偶爾會檢查一下代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該公司走了了相反的方向。他們希望實現一個單一的“立即購買”(Buy Now!)按鈕,它基本上是一個單步結賬。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/c1\/c1263156cb514f42d0d73c6d29fb0322.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於這明顯位於產品列表頁面,因此利益相關者要求產品列表團隊來開發。其中一個開發者看了這個需求。他們認爲,既然這個頁面都是關於產品的,我們應該像這樣在一個產品類中實現它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"class Product {\n public void CheckoutWithOneClick() {\n this.PaymentService.Pay(this.Price);\n }\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在經驗豐富的開發者看來,這是一種非常非常強烈的代碼異味。不知何故,它通過了代碼審查,被簽入(check in)併成爲單體的主要部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"六個月之後,所有結賬相關的事情都變得非常難以實現,而且漏洞百出。如果我們希望應用促銷代碼,我們需要記住在產品類別中實現了一鍵結賬。如果我們希望爲一個特殊類別的客戶提供折扣,我們也需要記住這一點。在許多情況下,結賬團隊被要求實現一些複雜的結賬方案,但這和這種一鍵按鈕的效果並不理想。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"特性開發、錯誤修復以及所有與結賬流程相關的內容都是單獨實施的。由於產品團隊擁有 Product 類,而結賬團隊擁有 CheckoutService,每個團隊對需求的解釋各不相同,業務邏輯的實現也各不相同。一種方法是首先檢查促銷代碼,另一種方法是檢查客戶特權,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當 John 一旦看到這一切的時候,就太遲了。CheckoutWithOneClick 與常規結賬流程之間存在許多代碼重複和獨立的邏輯分支。要重構這段代碼需要大量的投資。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在某個時候,John 被解僱了,而 Jane 聘請了一名新的 CTO。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jane 問的第一個問題是:爲什麼我們在結賬流程中做任何事情都這麼難?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新 CTO:嗯,一鍵結賬和普通結賬有不同的邏輯實現。這樣就很難在結賬流程中做任何事。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Jane :怎麼會這樣?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"團隊中的隨機工程師:每個人都知道這件事。它由一位開發者開發,他決定爲一鍵簽入提供單獨的邏輯。那太糟糕了,但我不知道 John 怎麼能做得更好。他不可能檢查每一段代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"新 CTO:好吧, John 應該從微服務開始。這樣的話,產品列表團隊將不會想到這樣的設計。他們會被結賬團隊的服務所有權所阻止。"}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"微服務的價值"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"嗯, John 處於一個奇怪的境地。如果他使用微服務,就會有厄運;但如果他不使用微服務,也會有厄運。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲微服務在本質上講是一種管理工具。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務可阻止某些類型的改變,並且使得某些類型的改變更加容易。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在第一個故事中,微服務架構阻止了所需的改變。在第二個故事中,由於缺乏微服務架構,導致了一系列不希望出現的改變。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就是爲什麼 John 無論如何都註定要失敗。問題不在於單體與微服務。而是問題在於架構選擇與業務的增長不一致。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以爭辯說,在第二個故事中, John 可以更加努力工作,確保所有的代碼都經過了設計審查和整個審計等等。但這纔是重點。這種管理方案的成本可能會很高,並在開發過程中會增加大量的開銷。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務能夠降低這一成本,因爲它本身很難讓設計混亂,所以我們不需要所有這些繁重的流程。但當我們真的想要把這個設計搞得亂七八糟的時候,好的,你已經從第一個故事裏學到了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,架構必須與業務增長保持一致。這就是我要講的全部。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在使用微服務時,你會讓某些增長變得更加容易,同時也會讓某些類型的增長更加困難。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有一個結賬的微服務,結賬團隊就能更快地推出新功能。他們自己可以發佈,能夠使用自己喜歡的編程語言,不依賴其他團隊,能夠獨立管理其服務的可擴展性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就是我認爲的,微服務架構的核心優勢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務的每一個技術方面,例如獨立擴展系統的某些部分或使用多語言實現,都可以用單體來解決。你可以使用一個構建,並將其配置爲某些容器打開特定的端點,然後你就可以獨立地擴展系統的某一部分了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在操作系統級別上使用跨進程通信,並管理二進制分佈,這樣就能用多語言實現了。也許延遲比網絡更好。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單體不能解決的問題在於,當你希望擁有某種類型的管理結構,並且通過給予自主權和頻繁的獨立發佈週期,從而激勵某個領域的發展和創造力。這就是在單體架構中,真的非常難以做到的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我想說的是微服務實施的決定性因素。它是否與你的組織想要發展的方式相一致?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於類似地,微服務從本質上說是對管理問題的一種技術解決方案。這個問題確實存在,需要很好的解決方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你覺得這是個愚蠢的管理問題,那麼我會邀請你來解決它。如果你能以可擴展和可重複的方式解決“如何使程序員有效協作?”這樣一個簡單的問題,我相信你有可能比傑夫·貝佐斯(Jeff Bezos)更富有。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"亞馬遜(Amazon)一直在做這方面的工作。谷歌、Facebook 也一直在做這個工作。現有的每一家巨無霸科技公司都已經在這方面工作了幾十年,並提出了多個獨立的服務產品(後來工業化成爲微服務)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你有更好的答案,那麼請繼續,讓我和所有這些人知道。他們已經準備好投資了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者介紹:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Chris,專門從事編程的產品開發者。堅信人文主義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文鏈接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https:\/\/medium.com\/chris-dialogue\/start-with-microservices-dont-start-with-microservices-db6cff6adb90"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章