查看原文
其他

我被微服务坑掉了CTO职位

Egor Romanov 程序猿DD 2023-02-18

来源:InfoQ、作者:Egor Romanov、译者:核子可乐、划:凌敏

初创公司在技术上到底要踩多少“坑”?  

Egor Romanov 曾被朋友邀请到一家初创公司担任 CTO,他先组建了一支工程师团队,然后着手构建后端、Web 管理门户和移动应用,并决定使用微服务架构来构建后端。不幸的是,这个初创业务未能获得市场关注,公司也在几个月后就宣布倒闭。回顾整个创业历程,Egor Romanov 总结了一些经验与教训。以下为他的自述。

空降 CTO 接手“烂摊子”

第一次受邀到初创公司担任技术主管的时候,我基本压根不清楚自己要做的是哪一行,但朋友的盛情实在难却。我认真审查了之前技术负责人和开发者着手构建的解决方案,他们做得不错,但受种种原因影响吧,大家最终决定退出项目。所以我得迎难而上,接手一款刚开发的产品,而且团队里根本没人可用。

这家初创公司开发的是一款应用程序,号称能帮助用户找到最划算的交易和合作企业,高效运用自己的时间和资金资源。这款应用程序预期把用户跟非旺季时段的库存或者产能过剩的公司对接起来,既帮助买方享受折扣、又帮助卖方回流资金。

项目要求在 iOS 和 Android 平台开发移动应用,还要搭配一套 Web 管理门户,供企业主管理产品、开展客户沟通。另外,所有采购行为必须通过我们的应用,不可私下交易。

因为团队不给力加上期限紧迫,我知道自己必须抓点紧了。

于是,我先组建了一支工程师团队,着手构建后端、Web 管理门户和移动应用。虽然我们已经拥有明确的开发目标和愿景,也制定了可靠的计划,但我知道愿景和计划肯定是会变的。另外,招聘合格工程师耗费的时间也比我预期中要多,所以得随时灵活调整推进策略。好在,我最终还是建立起了一支既有执行力和战斗力,又能不断适应环境变化的优秀队伍。

使用微服务构建可扩展后端

在刚开始构建后端时,我就知道可扩展性和适应性是其中的关键。经过广泛搜索,我找到了一位真正精通 Node.js 技能的后端开发人员。经过商议,我们决定使用微服务架构来构建后端。达成共识之后,我们开始根据业务要求和动态特性,逐步招聘移动和 Web 开发人员。

我在基础设施方面已经积累了一定经验,所以承担起了云、Kubernetes 集群、监控与日志记录,还有编码基础设施的设置任务。我们用 GitLab 进行版本控制,并使用 CI/CD 管线实现了构建、测试与部署流程的自动化。我们选择 JSON-RPC 作为通信协议,并指定 Node.js 作为后端。我们的后端开发人员又决定使用 MongoDB 数据库——虽然我个人更偏好 Postgres。

最终,我们定下了以下几项微服务:

  • Products:这项微服务用于管理由合作伙伴提供的产品和交易、他们的零售地点和促销活动信息。通过 Products,用户可以创建、更新和管理这些条目。另外,Products 还负责处理促销活动的启动与中止。

  • Business Users:这项微服务的作用,是在管理面板中管理 Auth 服务下的企业及其员工和数据。

  • Orders:负责实现订单的购物车和生命周期,并与支付系统相集成。

  • Gate:这项微服务位于从客户端到后端(也就是从移动设备到管理面板)的两个入口点处。它维护着客户端到后端之间的 websocket 连接,负责将请求定向至身份验证服务或者外观。

  • Admin Façade 和 User Façade 是后端的外观微服务。它们将来自客户端的请求分发给服务,负责封装系统的内部结构,而且会根据可用方法为客户端授权访问权限。

  • Auth:这项微服务负责用户的身份验证和授权。

  • File:这项服务负责管理静态资源(例如产品照片或者与合作伙伴的法律文件),而且跟 Yandex 集成以存储数据。

  • App Users:这项微服务负责管理移动应用用户。除其他事项外,它还存储用户的相关元信息:最后一次上线时间、好友等。

  • Settings:这项服务负责管理应用程序的设置。

  • Marketing:这项微服务负责管理营销 / 促销活动和推荐列表。

  • Email、Push 和 SMS 通知服务各自与对应的供应商集成。整个平台涉及多个外部集成:面向供应商 CloudPayments 的支付功能,用于 Push/SMS/Email 的通知服务,还有把静态文件(例如图片)匹配至 Yandex 云对象存储的功能。

我们的架构

我们用了两、三个月时间才找到一位移动 /Web 开发人员。但我们没有浪费掉这段时间,坚实的后端基础设施在此期间已经成功建立了起来。我们在开发过程中多次调整过概念和需求,而微服务架构大大降低了后端的调整难度。

得夸一句,我们的移动开发者非常棒。经过多次重新设计,最终移动应用充分满足了我们 CEO 和设计团队提出的每个新愿景。客户端应用程序和后端间的通信,通过使用 json-rpc 协议的 websocket 实现。前端使用的是 Vue.js,移动端使用的是 React Native,这有助于团队保持一致性和代码共享能力。

总的来说,我们几乎在一切可能的地方都在用 JavaScript,它能帮助工程师轻松理解同事编写的代码,并在必要时灵活更新服务和移动 /Web 应用程序的通信方式。

可能正确的答案 —Supabase

在当初建立这家初创公司时,我们发过一篇博客,讲述了我们在此期间面临的挑战。我们从社区收到了不少反馈,其中有些比较消极,但也有不少颇具建设性。对我来说,印象最深的一条反馈就是 建议用Firebase这类服务来简化我们的后端

当时,我个人觉得用 Firebase 不行,因为这可能引发严重的供应商锁定,没准会导致我们失去对数据和基础设施的控制权。注:几个月后,我们的初创业务未能获得市场关注,因此被迫放弃。在此期间,我在翻看 Y Combinator 时偶然发现了 Supabase。事后来看,可能最适合当初创业需求的就是 Supabase。

这是个开源平台,想帮助开发者简化为 Web/ 移动应用程序构建安全可扩展后端的过程。它以 Postgres 为基础,提供一系列工具和服务来管理数据库、身份验证、实时数据同步和存储对象,同时仍保证用户能控制自己的数据和基础设施。它的主要功能包括:

  • 自动生成 API:Supabase 能自动为 Postgres 数据库生成 REST、GraphQL 和实时 websocket 通知,帮助用户便捷访问来自 Web 和移动应用程序的数据。

  • 用户身份验证和授权:Supabase 内置支持用户身份验证和授权,能轻松保护应用程序和敏感数据。

  • 实时性:Supabase 能让 Web/ 移动应用程序同数据库保持同步,无需手动刷新数据。

  • 存储:用户可以存储大型对象,例如图像或文档,还可以通过请求调整图像大小。

  • 可扩展性与安全性:Supabase 构建在 Kubernetes 之上,具备自动扩展与高可用性,同时提供静态加密和传输中加密等安全功能。

总之,Supabase 堪称初创企业和小型团队的绝佳选择,能帮助用户轻松快捷地构建后端,而且不必费力设置和维护整个复杂的基础设施。即使身在大型公司,各位在推出新服务时也不妨考虑使用 Supabase 或者类似的开源项目。

如果我们当初选了 Supabase 会怎样?

下面我们就大开脑洞,畅想如果从一开始就选择了 Supabase,我们的初创公司会有怎样的不同。

首先,我们可以节省下来的业力投入到真正重要的工作中:关注用户、关注产品,而不是花几个月时间来构建微服务。从基础设施身上省下的时间不光能用于招聘移动开发人员,也能用来打磨后端。

Supabase 大大降低了数据库的设置和管理难度,它丰富的内置功能可以直接替代我们的大多数微服务。节约下来的不只有时间,更有宝贵的资金和精力。有了它,我们没准能保持住适应变化和需求的能力,这也是我们当初最引以为傲的本钱。但很遗憾,当时我们根本没听说过 Supabase——现在屏幕前的你知道了,它也许能改变你的创业故事。

如果用上了 Supabase,我们来看看原本的架构会发生怎样的变化。

 Auth

首先,Supabase 的内置身份验证和用户管理服务,可以直接替代我们的独立身份验证和用户管理微服务。它将为我们提供开箱即用的用户注册、登录以及用户角色 / 权限管理方案。另外,Supabase 还支持基于角色的访问控制(RLS),所以我们完全可以对数据进行细粒度访问。比如,用户只能查看自己的订单,企业主可以编辑自家产品,管理员则可以访问所有数据。

在 Supabase 中创建一项 RLS 策略,这样企业主就能轻松管理全体员工

 存储

使用 Supabase 的内置文件存储,可以简化文件上传、下载和管理的整个流程,不需要依赖任何单独的存储解决方案。不同于独立的 File 微服务,Supabase 的内置文件存储直接提供功能,例如调整产品图像的大小,并允许用户动态创建预览。另外,我们也能用 Supabase 的文件存储安全存储和管理业务用户签署的法律文件,无需额外第三方服务即可将这些重要文件集中起来存储和访问。

// Request a small resized image of a product from Supabase Storage:await storage.from('mama_jane').download('pizza.jpeg', { transform: { width: 200, height: 200, format: 'origin', },})
 存储

使用 Supabase,Gateway 和 Façade 这两项负责处理移动应用和 Web 应用同其他微服务间通信的设计也就不需要了。Supabase 的自动 PostgREST 和 GraphQL API 能够完全承担起这方面需求,让我们专注于其他更有价值的工作。

# Retrieve a feed with products from app using Supabase generated GraphQL API{ retailersCollection( filter: {active: {eq: true}}, ) { edges { node { name productsCollection { edges { node { id title imageUrl price } } } } } }}
 Gateways 与 Facades

关于 Product 和 Settings 微服务,它们主要是充当数据所有者,处理关于产品的各项 CRUD 操作。但如果能使用 Supabase,我们可以直接跳过这些麻烦事、享受 Postgres 的强大功能。这样,我们就能直接在数据库中处理其他更复杂的操作,例如涉及交易事务的产品更新。而且 Supabase 还提供内部 hook 和 cron 作业,并全面支持 pg_cron、触发器、webhook 以及无服务器函数等。

 Notifications

我们可以用 Supabase 提供的表上无服务器函数和触发器,顺利替代当初设置的 Push、SMS 和 Email 通知微服务。例如,我们可以在订单表上设置触发器,确保订单确认时立即向用户发送推送、短信或者电子邮件通知。我们可以使用触发器在某些事件发生时延动怒出消息,例如创建用户账户或添加新产品。

这将大大降低整体架构的复杂性,使其更易适应不断变化的需求。想象一下,如果我们的营销经理想要组织一场促销,并向过去 30 天内从未下单的用户发送推送通知。利用 Supabase,只需在订单表上创建一个简单的触发器即可轻松实现。

 营销活动

前面的例子同时证明,我们的 marketing 服务也没必要独立存在了。因为我们可以设置由特定操作触发的自动营销活动,或者引入名为 marketing_campaigns 的新表。之后,营销经理只需要向表内插入一个带有参数的新行——比如作为通知目标的用户。表上的触发器将自动调用无服务器函数以发出推送通知。

--Trigger to send push notifications after the--marketing specialist adds a marketing campaign to the database.create or replace function insert_marketing_campaign() returns trigger as $$begin insert into marketing_campaigns (message, user_group, start_date) values (new.message, new.user_group, new.start_date); perform send_push_events(); return null;end;$$ language plpgsql;
create trigger insert_and_send_pushafter insert on marketing_campaignsfor each rowexecute function insert_marketing_campaign();Admin studio

回想起来,为企业客户专门构建自定义管理门户可能也不是什么好主意。虽然我当初提出过反对,但合作伙伴那边坚持说有必要。而事实证明,我们的客户并没有做好迎接这样一套新的、完全陌生的界面的准备。Supabase 仪表板允许销售团队轻松管理各企业客户的产品。也许我当初该再加把劲,劝合作伙伴在吸引到更多用户并切实了解他们的需求之前,先别急着搞一套独立的定制化管理门户。

 营销活动

最后要聊的是 Orders 服务。单从技术上讲,我们本可以用无服务器函数加触发器来替代,但我还是选择用老办法。原因是我这人就这样,比较敏感守旧。但即使如此,如果选择 Stripe 作为支付供应商,我们也能用上 Supabase 的打包器新功能。

此功能使用 postgres_fdw 从 Postgres 直接向 Stripe 发送查询,由此轻松完成交易。如果还有额外的时间和余力,我们还可以针对 Stripe 开发自己的打包器并把它跟 Supabase 整合起来。

// Listen for stripe events using Supabase Edge Functions // (to keep track of invoice-paid events, for example)serve(async (request) => { const signature = request.headers.get("Stripe-Signature"); const body = await request.text();
let receivedEvent; try { receivedEvent = await stripe.webhooks.constructEventAsync( body, signature!, Deno.env.get("STRIPE_WEBHOOK_SIGNING_SECRET")!, undefined, cryptoProvider ); } catch (err) { return new Response(err.message, { status: 400 }); }
console.log(receivedEvent); return new Response(JSON.stringify({ ok: true }), { status: 200 });});
 Orders

我们这家初创公司还高度依赖于地理数据。我们本可以使用 PostGIS(Postgres 的空间数据库扩展程序)的强大功能处理全部地理数据需求,这样就能轻松将基于位置的搜索和映射等功能整合到业务应用程序当中。

总的来说,使用 Supabase 加 PostgreSQL 这套强大组合,将大大简化我们的业务架构,让我们腾出时间和精力专注开发应用程序的核心功能。使用 Supabase 稻可能改变我们这家年轻企业的命运——虽然不一定保证获得商业成功,但至少能节约下可观的开发和基础设施运营资金。

如果使用 Supabase,那我们的架构可能会是这个样子:

更重要的是,我不用担心供应商锁定问题了。用户可以配合多种编程语言使用 Supabase,包括 JavaScript、Dart、Python 或者 Go,极大提高应用程序的构建和维护灵活度。另外,Supabase 在设计上很重视扩展需求,既适合小型初创公司、也能满足大型企业客户。它同时支持云端和本地环境,还能跟其他开源项目集成,这就为应用程序构建和部署工作赋予了极高的自定义空间和灵活性。

写在最后

回顾整个初创历程,我们意识到 构建微服务架构是个既充满挑战又成本高昂的决定。但是,Supabase 提供了一种更简单、更具成本效益的替代方案,其内置功能完全可以满足典型初创企业对多种微服务的需求。从用户管理到文件存储、再到实时 API 和自动数据管理,Supabase 有望帮助大家显著节约时间和资金。

虽然我当初没能在自己的初创公司中使用 Supabase,但我希望自己的这段惨痛教训能给大家带来一点启示,特别是认真考虑 Supabase 在公司业务环境中的可行性。诚然,Supabase 不可能是所有项目的最佳选项,但至少值得一试,而且大家最好能在市场上广泛物色可能更适合需求的其他替代方案。在我看来,Pocketbase 在某些方面可能表现更好(比如某些涉及大量自定义 Golang 代码的基础设施开发项目),但总体上 Supabase 更适合大多数项目。

原文链接:https://egor-romanov.medium.com/building-a-startup-from-scratch-my-mistakes-as-cto-b20b463e0058


------
我们创建了一个高质量的技术交流群,与优秀的人在一起,自己也会优秀起来,赶紧点击加群,享受一起成长的快乐。另外,如果你最近想跳槽的话,年前我花了2周时间收集了一波大厂面经,节后准备跳槽的可以点击这里领取

推荐阅读

··································

你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。

点击领取2022最新10000T学习资料

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存