01项目概述
Admister是一个多垂直市场。在一个屋檐下,它运行分类信息、汽车和零件板块、房地产买卖和租赁、带有卖家商店的商品市场、数字商品、优惠券服务和价格聚合器。每个都有自己的子域名、自己的首页和对商品的独特理解。
从外部看,它们像是七个独立的产品。从内部看,它们是一个应用程序:服务由请求的主机名决定,所有底层的内容——账户、资金、审核、搜索、媒体、通知——都是共享的。这个项目是我们从头到尾负责的:模型、引擎、界面和每个垂直领域的发布。
02背景和问题
启动多个市场的常见方式是启动多个网站。这种方式在第二个网站之前是有效的:现在有两个用户表、两个审核队列、两个支付集成和两个地方需要修复同样的错误——而且要以不同的方式修复,因为到那时代码已经漂移。
相反的极端同样糟糕。一个名为“汽车”的类别的网站无法像汽车板块那样询问里程、发动机和车牌,而一个无法按楼层和房间过滤的房地产搜索就不是一个房地产搜索。任务是保持一个引擎,而不假装七个非常不同的目录是同一个目录。
03项目目标
- 一个代码库和一个账户用于所有垂直领域,服务由主机名选择;
- 每个垂直领域的属性和过滤器:汽车和房地产的数十个字段,彼此之间没有任何拖拽到其他服务中;
- 业主可以信任的资金 — 余额、加密发票、幂等信用和滞留付款的对账;
- 吸引买家回来的理由:带推送的保存搜索、卖家徽章、拍卖和团购;
- 两种语言和四种显示货币,无需重复内容;
- 一个与后端前端通信的移动应用,而不是抓取网站。
04我们做了什么
我们将服务注册表构建到启动序列中。请求到达时,主机名与注册表匹配,从那一刻起,应用程序就知道它属于哪个垂直领域:加载哪些模块,使用哪个属性集,渲染哪个首页和哪个菜单。裸域是分类广告板,因此最短的地址也是最通用的地址。
所有非垂直特定的内容只编写一次:账户和会话、余额和付款、媒体接收、审核、保存的搜索、通知、网站地图和API。添加第七个服务主要是编写其属性和首页 — 而不是另一个市场。
05七项服务,七个目录
- 分类广告 — 综合板块:任何东西,从自行车到沙发,免费发布。
- 汽车 — 车辆、车牌和零件,拥有专门的属性集,包括数十个字段和过滤器。
- 房地产 — 出售和出租,拥有自己的属性集、地图和按地区搜索。
- 市场 — 带有卖家商店的商品:每个卖家的店面、库存、订单。
- 数字 — 密钥、礼品卡和eSIM:以交付方式提供的商品,而非运输。
- 交易 — 合作商店的优惠券和促销代码。
- 价格 — 一个价格聚合器:在商店之间比较相同的产品,并计算出站点击数。
他们共享账户、余额、审核队列和搜索索引 — 但在属性上没有任何共享,这正是关键所在。
06资金:余额,加密发票,调解
付费功能 — 促销、商店计划、数字商品 — 从内部余额中支付,余额通过 Payora 充值,我们自己的网关。发票主体在一个函数中构建,因为相同的主体在相同的幂等性键下被对账 cron 重放:两个之间的单个字段如果不同将导致冲突,使支付悬而未决。
我们在任何网关上都会重复的两个细节。发票携带买家的本地语言,取自他们的账户行,而不是来自网络请求 — cron 没有请求,否则会将所有人发送到英语页面。并且它携带一个返回地址,指向裸域上的账户,因此从汽车垂直领域开始的买家会以登录状态返回,而不是返回到一个要求他们再次登录的页面。
07信任:审核,徽章,争议
一个市场的评价取决于其最差的列表,因此这里的审核并不是一个附带的环节。每个决定——批准、拒绝、下架——都由一个处理者负责,而「拒绝」故意与「下架」不同:前者是对作者的有理由的回答,后者则是移除已经上线的内容。两个动词,两个不同的字母,记录中的两种不同状态。
卖家和商店获得的徽章是通过努力获得的,而不是购买的:账户存在多久,完成了多少交易,手机和电子邮件是否已确认。买家在卖家出现的每个地方都能看到相同的徽章,因为它是在一个地方计算的。
08搜索,保存的搜索和推送
搜索知道自己处于哪个垂直领域。在汽车板块,它提供汽车的各个字段;在房地产中,它提供房间、楼层和区域;在综合板块,它保持故意简单。建议来自于自己的端点,因此在页面仍在被阅读时,字段就会作出回应。
搜索可以被保存——这就是将访客转变为回访者的原因。新的匹配结果以网页推送的形式到达,订阅属于账户而不是浏览器标签,而同样的机制也传递人们实际等待的事件:即将结束的拍卖,达到阈值的团购。
09一个拥有自己后端的移动应用
该应用程序并不抓取网站。它与自己的前端后端进行交互:一小组端点以屏幕所需的格式进行响应,令牌与网页会话分开发放。有两个路径在没有令牌的情况下开放——应用在任何人登录之前所需的参考数据,以及一个健康端点。
该健康端点是故意设置为一个单独的地址。监视整个系统的注册表曾经会询问参考端点,而那个端点会汇总每个字典和每个速率——活跃性检查不应花费超过工作量。它还会返回合同版本,因此为旧合同构建的客户端会在用户注意到之前被发现。
10七个SEO前端
一个域名上的七个服务意味着七个网站地图,七组结构化数据,以及一种非常简单的方式来产生重复内容。每个垂直领域从缓存中发布自己的网站地图,列表页面声明自己的结构化数据类型,而搜索结果页面从不声称是列表的规范。
内容方面也是自动化的:一个编辑流来自 Astrina,这是我们为此构建的平台,文章一旦到达,其垂直领域的网站地图缓存就会被清除——否则新页面将等待下次重建,没人会知道它为什么缺失。
11一个外部渠道
应用程序向外发送的所有内容——API调用、网络钩子、合作伙伴数据源、获取的图像、邮件——都通过一个绑定到SOCKS5出口代理的单一模块进行,DNS也通过代理解析。该模块是故障关闭的:如果代理不可用,请求会被阻止,而不是悄悄地从服务器自己的地址发送。
这不是装饰。一个市场整天拉取合作伙伴数据源和远程图像;每一个请求都会将网站的源地址传递给第三方。邮件也是如此,通过 YourTrend因此,信件是签名的,服务器的主机名从不出现在头部中。
12技术栈
- 没有框架的 PHP 8.3,一个薄的 PDO 封装和到处使用的预处理语句;
- MariaDB,模式保留为编号迁移文件,而不是某人的记忆;
- 媒体存储在 web 根目录之外,并由控制器提供服务,因此上传永远不是可执行路径;
- 一个构建步骤,哈希资产并内联不应被缓存的内容,并在其表现不佳时有一个杀开关;
- 每个出站调用的 SOCKS5 出口,通过 YourTrend SMTP 发送邮件;
- Payora 用于加密发票,Astrina 用于登录和编辑内容,Adgora 用于内部广告。
13结果
在一个代码库上有七个实时服务,一个账户和一个余额,所有服务之间的属性和过滤器与目录一样不同。一个新垂直是一个属性集和一个首页——而不是一个新项目,不是第二个用户表,不是第二个支付集成。
所有者每天获得的就是一个查看的地方。管理、资金、卖家和内容都通过整个组的共享控制面板进行管理,市场通过签名的管理员 API 而不是通过自己的第二个管理员网站来暴露它们。
14要点
我们将带入下一个市场的教训:分享机器,而不是意义账户、资金、适度和搜索在每个垂直领域都是同一个问题,必须只写一次。属性、过滤器和列表的形状就是产品本身,将它们强行放入一个通用表格是市场对每个人同时变得不可用的原因。
第二个教训是关于资金的。描述支付的地方必须是一个单一的功能,因为其他东西会重放它——一个定时任务、一个重试、一个对账——而对一个支付的两个描述是悄悄失去资金的最便宜方式。
如果您正在构建一个市场、分类广告板或一个基于单一引擎的多垂直平台,这就是我们从头到尾所做的工作。



