前后端分离的边界在哪 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
zioc
V2EX    程序员

前后端分离的边界在哪

  •  1
     
  •   zioc 2017-05-14 04:53:07 +08:00 6945 次点击
    这是一个创建于 3119 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有一句话说业务逻辑后端做,渲染逻辑前端做。

    具体怎么理解呢?

    例如表数据如下:
    id name pid
    0 广东 -1
    10 广州 0
    11 深圳 0
    21 天河 10
    22 南山 11


    前端需要的格式如下:


    A 方案后端递归处理数据最后输出如上,前端直接使用


    B 方案服务端直接按照表结构给数据,前端递归,格式如下:


    B 的好处是:
    1 后端输出简单,节省服务器资源
    2 源数据结构,不管页面怎么改,restful api 都不用改
    3 分离清晰,后端只提供数据



    是 A 还是 B 呢?
    26 条回复    2017-05-16 10:12:07 +08:00
    geelaw
        1
    geelaw  
       2017-05-14 05:00:58 +08:00
    A 方案怎么分页?每次重新返回整个链条?

    如果可以保证 pid < id,那么用 B 显然可以很简单地处理分页。

    另外如果数据出现不一致(譬如有环),如果用 A 会更容易导致后端崩溃,如果是 B 那么后端很难崩溃。毕竟,一个顾客心肌梗死和一个商店塌了相比,还是商店塌了更可怕一些。

    还有另外的考虑:如果分级的层数是固定的,那么你的 model 可以变化为固定深度的,那应该用 A。
    binux
        2
    binux  
       2017-05-14 06:19:13 +08:00
    我觉得你这个表首先设计就有问题,它天生带有一个需要递归查找所有的 children 的逻辑在里面。
    如果你每次都要全读出来,那你还搞这个 pid 干嘛,直接存成 A 不就好了。
    如果你要去遍历,难不成前端读完广东,还要再去读 where pid = 0 吗?

    所以根本原因是你这个表设计得有问题。
    think2011
        3
    think2011  
       2017-05-14 06:28:17 +08:00
    跟前同事都是这样约定的

    1. 前端负责展示,后端负责数据
    2. 后端要是处理数据太麻烦,丢给前端整理
    3. 前端要是运算数据太麻烦,丢给后端运算

    基本上就 1 + 2,楼主的这种情况,通常是 2 的方案
    ywgx
        4
    ywgx  
       2017-05-14 10:10:31 +08:00
    在安全性考虑的前提下,一般会尽量把计算量放到前端,分摊服务端压力
    当然如果设计上前段解决起来,异常复杂,导致简单问题复杂化,就放在后端吧
    这个灵活度比较大,还是具体项目具体实施吧~
    reus
        5
    reus  
       2017-05-14 10:30:31 +08:00   2
    当然是 A,用 B 的后端也太懒了吧,什么都丢给前端。就想着自己有好处,想过前端怎么骂你吗?
    前端要树,我就给树,前端要表,我就给表。分成两个 api。我不用 restful,一个 api 干什么的,我好好命名,不搞那套偷懒甩锅的把戏。
    节省服务器资源?构造一个树,纯粹的计算,没有 io,能用你多少资源?找借口偷懒而已。
    klesh
        6
    klesh  
       2017-05-14 10:50:53 +08:00   1
    A
    前端不应关心数据如何存储,既然这是一个树形结构就要返回一个对应的树形结构。后端应保持数据结构的统一表述和抽象隔离。现在是一个表结构,以后是一个文档结构呢?使用产后端分离最大的诉求之一是前端可能有无数个,从种类上分有 spa / mpa / iOS app / android app / 各种平台的 app。若在前端进行递归整理,工作量和维护量会增多。反之,你只要在后端维护一个树形结构,就可以节省很多前端的工作量以及可能出现的 bug。

    1. 节省服务器资源的问题不存在,后端必须做 cache,否则只要有某一些前端 /app 调用不合理后端就跨掉?
    2. 源数据结构不改页面就不用改?那要看你怎么定义源数据结构这个概念了。是从业务层面上去看这个问题,还是从数据库层面上去理解。
    3. 我觉得可以用『去餐厅吃饭』这件事来类比这个事情。对于一个树形结构,直接返回原始底层数组,就相当于餐厅把原材料给你上上来,然后叫你自己拿回家去煮。

    以上,你说的三条好处全部站不住脚,这不是后端偷懒的理由。即使你们要按哪端去处理比较方便,也得以『前端会有很多个 app 』这样的前提去考虑,以后端必须做 cache 为前提去考虑,以业务逻辑和关注点分隔的原则去考虑。

    再扩展一下话题,若将来你们要集成第三方平台,然后他们的 location id 与你们的 location id 不一致要怎么处理?若『后端只给数据』这个是可以接受的话,然后是要把第三方的 id 也返回给前端,让所有的前端去处理吗?


    @geelaw location selection 最多做一个联动选择界面,为什么要分页?分页之后只有一部分的数据,如何正确重建树形结构(如何说某一个 parent 没返回来过,但它的 children 返回了)?


    @binux 有意思,表应如何设计?
    coderfox
        7
    coderfox  
       2017-05-14 10:51:07 +08:00 via Android
    原始数据的结构是树,只是数据库设计上表现为表,所以应该用树。

    如果后端有压力可以尝试使用缓存。

    我理解的前后端分离是:后端提供的接口呈现数据的原始模式。
    reus
        8
    reus  
       2017-05-14 11:10:08 +08:00
    @binux 没问题,不需要递归,一般是把所有行读出来,构建树结构,然后返回客户端。这个树可以缓存起来。这样查询和更新都很方便。直接在数据库存树,更新不方便。
    geelaw
        9
    geelaw  
       2017-05-14 11:18:51 +08:00
    @binux 然而似乎没有什么好的策略用于存储层数任意的树。当然题主给的这个例子里面层数是有限的。

    @klesh 所以我说如果 pid < id 且按照 id 升序返回那就不会有问题啊。

    @klesh @coderfox 为什么有根树的森林就一定要是递归的样式?有根树的森林不可以是符合一个约束的父亲数组存储?
    ezreal
        10
    ezreal  
       2017-05-14 11:18:56 +08:00
    A
    klesh
        11
    klesh  
       2017-05-14 11:53:19 +08:00
    @geelaw
    "所以我说如果 pid < id 且按照 id 升序返回那就不会有问题啊"
    第一,即使你的假定可以做到,分页的意义在哪里?第二,你的假定根本不可能做得到,行政区的划分并不是一成不变的,一定会存在 pid > id 的情况:如新区成立, 老的村镇其 pid > id

    "为什么有根树的森林就一定要是递归的样式?有根树的森林不可以是符合一个约束的父亲数组存储?"
    这里说的不是存储的问题,而是前后端数据结构交换的问题。前端不应关心后端如何存储数据,后端不应关心前端如何展示数据,两者之间通过合理的数据结构进行交流。
    geelaw
        12
    geelaw  
       2017-05-14 12:04:49 +08:00
    @klesh

    前一个问题:可以通过一开始取较大的 gap 避免,没说 id 必须是连续产生的;还有一种方法是准备另一个排序的 column,然后修改那个 column 以便以后按照“好”的顺序返回结果。(吐槽:行政区划这点数据还犯不着纠结这个问题,而且行政区划已经是有限层的问题了,不需要用这种组织形式存储。)

    “前端不应关心后端如何存储数据,后端不应关心前端如何展示数据”

    这个和你说用 A 不是矛盾的么?按照这个想法,A 和 B 都没有天然的优势,因为 A 是按照前端想要展示的方式准备数据,B 是按照后端立刻就可以得到的格式准备数据。

    或者可能是我对“展示”的理解和你不同?在我看来数据以一个特定的逻辑形式存储出来(例如按照某种对象的形式)已经算是“展示”了,最后出现在屏幕上的方式不重要,因为从数据的逻辑存储形式到人可以通过不同的介质(常见的:屏幕、声音、编程)。或者说我认为前端和后端对接的部分是 model,而不是 view / view model,前端可以自己把 model 变成别的 model、view model。

    如果把“展示”理解为打印到屏幕上,那前端和后端之间的 gap 可太大了……
    int64ago
        13
    int64ago  
       2017-05-14 15:22:32 +08:00
    这种问题都是前后端彼此太狭隘造成的,如果一个人前后端都写,就会很自然的选出代价最小最优雅的方式,作为后端出生的前端,我更希望跟懂点前端的后端合作,大家很清晰如何分工

    对于狭隘的后端恨不得把整个数据库直接查出来丢出来,特别是用 Java 的,那些又长又臭的数据结构都是一坨一坨扔出来的(想保证一行 80 字符都难)

    对于狭隘的前端恨不得拿到数据直接展视(既然后端给了数组,为何还要给个 length ?),后端应该是尽可能给出简洁的**元数据**,因为很多时候暂时的时候顺便处理的一些数据计算是很自然的,放后端需要额外处理


    对于楼主的问题,我觉得应该放后端处理,倒不是考虑遍历的性能代价(其实这点计算双方代码量和执行效率都差不多)。因为这种结构有很强的依赖性,这种依赖性从前端看是完全由 pid 决定,而这个太不靠谱,如果数据结构重新组织,肯定是前后端都需要改了,而且 pid 的规则对于前端有点类似于魔法的感觉。再换一个大胆的构想,现在你用关系型数据库,于是很方便返回列表,后面假如换成了 mongo 呢,会发现直接返回递归形式更方便,而这些不应该是前端关心的
    binux
        14
    binux  
       2017-05-14 18:02:37 +08:00
    @reus #8 然而你这个例子里面,树结构是不会变的,不会有更新需求。即使变了,增加一个同名节点比修改指向更简单。
    maomaomao001
        15
    maomaomao001  
       2017-05-14 18:49:48 +08:00 via Android
    lodash.js
    sagaxu
        16
    sagaxu  
       2017-05-14 20:47:40 +08:00
    我来提一个概念,中端,有的公司把中端归为后端,也有公司把中端归为前端,最有代表性的中端语言就是 php。
    后端负责基础数据和基础业务逻辑的 API,中端组合后端 API 提供具体业务 API,并按前端的要求的格式返回。
    fohuhu
        17
    fohuhu  
       2017-05-14 21:08:10 +08:00
    有意思的帖子,公司目前也在谋划前后端分离
    learnshare
        18
    learnshare  
       2017-05-14 21:18:59 +08:00
    前端不应该做类似数据库查询这种的动作,这增加了前端的复杂度,数据结构最好能够由后端处理完成。
    当然,如果后端能够把数据库核心操作和 API 服务再分离一次,这种操作就有地方去了
    x464744246
        19
    x464744246  
       2017-05-15 09:11:00 +08:00   1
    这种省市级 其实建议取消 pid 直接类似
    id name
    11 广东
    1101 广州
    110101 天河
    类似 直接取位数匹配就好
    x464744246
        20
    x464744246  
       2017-05-15 09:11:36 +08:00
    @x464744246 然后 就直接 B 方案
    bengle
        21
    bengle  
       2017-05-15 15:10:08 +08:00
    前端更偏向交互,后端更偏向业务逻辑。理论上应该是这样,但是现实中只能各种撕逼。。。所以需要一层前后端对接的中间层,后端保持一种数据结构输出,中间层把后端数据转成前端需要的结构,这个中间层可以后端搞也可以前端搞,我厂是前端做的,诶~
    johnpang
        22
    johnpang  
       2017-05-15 15:54:39 +08:00
    [
    {"1":{"name":"广东","pid":0}},
    {"2":{"name":"广州","pid":1}},
    {"3":{"name":"天河","pid":2-1}},

    ]


    在已知层级数的情况下这样会不会好点呢。。。
    zioc
        23
    zioc  
    OP
       2017-05-15 18:39:52 +08:00
    @x464744246 实际业务类型并不是省市
    @bengle 感觉现在前端做更合理,或者叫客户端
    @learnshare 如果把前端换成 iOS/安卓客户端呢,应该客户端做吗
    @sagaxu 这个沟通成本更高了,如果中端的工程师水平一般或者对接口质量没有追求,那简直是一场灾难。我前公司就有中间件。
    @ywgx 比较同意,而且前端处理起来并不复杂
    learnshare
        24
    learnshare  
       2017-05-15 19:55:44 +08:00
    @zioc 移动端也不做
    类似的数据几乎是永远不变的,服务器生成一份缓存起来,以后就都是直接返回了,代价真的小
    zioc
        25
    zioc  
    OP
       2017-05-16 09:41:38 +08:00
    @learnshare 如果数据是变得呢
    learnshare
        26
    learnshare  
       2017-05-16 10:12:07 +08:00
    @zioc 变得也有理想的改变周期
    比如估计一天才改动一次,那缓存 1 小时,改动后更新缓存。类似的设计
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5197 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 08:13 PVG 16:13 LAX 00:13 JFK 03:13
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86