点赞表问题:我们为何采用多态

发布: (2026年2月22日 GMT+8 00:26)
4 分钟阅读
原文: Dev.to

Source: Dev.to

介绍

几天前,我在给一个应用添加 Community(社区)板块。用户需要能够:

  • 创建帖子
  • 留下评论
  • 为帖子点赞
  • 为评论点赞

我们还有一个独立的 News(新闻)板块,新需求是用户也应该能够为新闻文章点赞。

初始方案:分别的点赞表

如果只能对一种实体(例如新闻)进行点赞,模式会非常简单:

CREATE TABLE news_like (
    user_id   BIGINT   NOT NULL REFERENCES users(id),
    news_id   BIGINT   NOT NULL REFERENCES news(id),
    liked_at  TIMESTAMP NOT NULL DEFAULT now()
);

因为我们有三种实体——posts(帖子)、comments(评论)和 news(新闻)——我们考虑创建三张独立的表:

  • post_likes
  • comment_likes
  • news_likes

每张表都会有适当的外键关系,从而得到:

  • 强大的关系完整性
  • 简单的联接
  • 明确的结构

虽然这是最“纯粹的关系型”做法,但感觉重复。将来若再加入可点赞的实体,就需要再建一张表,导致模式水平膨胀。

多态点赞表

我们没有使用多张表,而是创建了一张 多态likes 表,能够引用任意资源类型。

CREATE TABLE likes (
    user_id       BIGINT      NOT NULL REFERENCES users(id),
    resource_id   UUID        NOT NULL,
    resource_type TEXT        NOT NULL CHECK (resource_type IN ('POST','COMMENT','NEWS')),
    liked_at      TIMESTAMP   NOT NULL DEFAULT now()
);

列说明

  • user_id – 点赞者(外键指向 users)。
  • resource_id – 被点赞项目的 UUID(没有外键)。
  • resource_type – 项目的类型(POSTCOMMENTNEWS)。
  • liked_at – 点赞的时间戳。

(resource_id, resource_type) 这对组合唯一标识被点赞的实体。该设计让我们只维护一张集中式表,易于扩展并在系统中复用。

权衡

主要的缺点是失去了对 resource_id 的直接外键约束。PostgreSQL 不能对指向多张表的外键进行强制约束,因此引用完整性必须在应用代码中处理。

统计特定资源点赞数的示例查询:

SELECT COUNT(*)
FROM likes
WHERE resource_id = 'some-uuid'
  AND resource_type = 'POST';

如果需要同时获取资源详情和其点赞数,可能需要单独的查询或在应用层加入逻辑。对我们而言,这种权衡是可以接受的,因为我们并不会在点赞上进行大量跨实体的联接查询。

结论

关键的洞察是 “点赞”是一种跨资源的行为,而不是仅与帖子、评论或新闻紧耦合的特性。以多态方式建模把 “点赞” 当作可复用的系统能力,从而减少了模式重复,并在应用增长时降低了后续重构的工作量。

0 浏览
Back to Blog

相关文章

阅读更多 »

Undefined 与 Not Defined

Undefined undefined 是 JavaScript 中的一个特殊关键字。它表示变量已经在内存中存在,但尚未被赋值。在创建阶段…

HTML基础介绍

HTML是什么?HTML是Hyper Text Markup Language的缩写。它用于创建网页并在互联网上组织内容。你看到的每个网站都是由它构建的……