零零客微文大全00ke.net

  • 烦死了,业务代码老写不好..
  • 来源:51CTO技术栈

本文举一个非常简单的例子,以案例的业务实现来分析如何写好业务代码。


图片

图片来自 Pexels


本案例只是简单的模拟,可能与真实的情况有出入,这里只是为了举例使用。


案例:用户挑选商品放入购物车,然后下单结算。


流程如下:挑选商品→下单→结算→生成订单→通知。


提交下单的业务逻辑如下:验证账号是否合法→调用第三方接口查看商品的打折价格→钱包金额扣除→生成订单信息→通知用户下单成功,等待收货。


代码实现:

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     *  购买商品,提交订单
     * @param userId      用户ID
     * @param productId   商品ID
     * @return
     */

    public Result submit(Long userId, Long productId) throws BizException {
        // 验证账号
        UserDO userDO = userMapper.findById(userId);
        if (userDO == null) {
            throw BizException(USER_NOT_EXISTS);
        }
        // 查看商品信息及打折信息
        ProductDO productDO = productMapper.findById(productId);
        Double delta = HttpUtils.getDiscount(productId);
        double actualPayment = productDO.getPrice() - delta;
        Money money = userDO.getMoney();
        if (actualPayment > money.getRemain()) {
            // 如果商品价格 - 优惠价格 > 用户钱包,则说明不够付
            return Result.fail("余额不足");
        }
        // 钱包够付,扣除金额
        double remain = money.getRemain() - actualPayment;
        money.setRemain(remain);
        // 更新账号钱包余额
        userMapper.update(userDO);
        // 生成订单信息
        OrderDO orderDO = new OrderDO();
        orderDO.setUserId(userId);
        orderDO.setProductId(productId);
        orderMapper.save(orderDO);
        // 通知用户订单已生成,等待收货
        kafkaTemplate.send("orderTopic", orderDO);
        return Result.ok();
    }
}

上面代码写好了,而且可以实现相关功能,但是随着业务的迭代,可能会出现很多问题。


①可维护性差


XxMapper 是基于 Mybatis 实现数据操作层,也就把技术细节带入业务逻辑中了,如果技术实现变了(改为使用 Hibernate,或 Mybatis 版本升级造成用法改变等),业务代码就得改变。


XxDO 是和数据表绑定的,数据表结构变更等也会影响业务代码。


调用第三方 API,直接在业务代码中调用 HttpUtils 完成,未来第三方 API 修改了方法签名或返回值,或改为了 RPC 接口,那么业务代码也会随着改变。


发送消息直接使用 KafkaTemplate,如果技术选型变了要改为使用 RocketMQ,那么业务代码还得变。


②可扩展性差


如果商品因为做活动又加了其他的优惠,或商品某一段时间不打折了,那么原有的代码就会重新改来改去。


业务逻辑和数据存储结构是强依赖的,数据存储结构的变化对业务的影响可想而知。


③可测试性差


因为直接依赖了数据库,第三方接口,中间件,所以需要所有技术实现后才能进行测试,测试成本和时间都比较大。


代码优化一


我们上面说了,数据库操作不应该直接暴露在业务逻辑中,因此把数据库操作“隔离”开。

public interface UserRepository {
    User findById(Long userId);
}

新增 XxRepository 接口,业务逻辑直接依赖接口/抽象,而不应该直接依赖实现。


Repository 是数据仓库,不一定非得是 DB,也可以是其他的数据操作。


Repository 返回的对象也不是 DO,与数据库结构无关。


代码优化二


DO 对象是只有 set、get 操作,没有其他行为,我们说这有时是一种贫血现象,会导致本该在业务领域实体中完成的事情散落到各个 Service 中,低内聚而且也不好维护。

增加领域实体,相关行为直接在实体内完成(高内聚):

public class Money {
    private double remain;
    public double getRemain() {
        return remain;
    }
    public void setRemain(double remain) {
        this.remain = remain;
    }
    /**
     * 扣费
     * @param delta
     * @return
     */

    public boolean charge(double delta) {
        if (remain < delta) {
            return false;
        }
        this.remain -= delta;
        return true;
    }
}

代码优化三


第三方接口是不可靠的,方法签名或返回值或调用方式都有可能会变的,如果直接在业务中依赖,会对业务造成“腐蚀”,所以应该加一层适配层(也叫防腐层 ACL)。

/**
 * 防腐层/适配层
 */

@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private DiscountFacade discountFacade;

    /**
     *  支付
     * @param money
     * @param product
     * @return
     */

    public boolean pay(Money money, Product product) {
        // 获取优惠
        Double delta = discountFacade.getDiscount(product.getId());
        // 扣除费用
        return money.charge(product.getPrice() - delta);
    }
}


代码优化四


抽象中间件,不直接依赖具体的 MQ 实现:

public interface MessageProducer<TR{
    Result<R> send(T message);
}

总结


优化后的代码如下:
@Autowired
private UserRepository userRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private MessageProducer<Order,Result> messageProducer;
@Autowired
private PayService payService;

/**
 * 购买商品,提交订单
 * @param userId      用户ID
 * @param productId   商品ID
 * @return
 */

public Result submit(Long userId, Long productId) throws BizException {
    // 验证
    User user = userRepository.findByUserId(userId);
    if (user == null) {
        throw BizException(USER_NOT_EXISTS);
    }
    // 支付
    Product product = productRepository.findById(productId);
    boolean f = payService.pay(user.getMoney(), product);
    if (!f) {
        return Result.fail("费用扣除失败");
    }
    // 更新账户
    userRepository.update(user);
    // 生成订单信息
    Order order = OrderFactory.create(user, product);
    orderRepository.add(order);
    // 通知用户订单已生成,等待收货
    messageProducer.send(order);
    return Result.ok();
}

代码不一定非常严谨,只是通过这一个简单的例子告诉大家实际工作中代码该怎么写,该遵循哪些目标。


①独立于框架:架构不应该依赖某个外部的库或框架,不应该被框架的结构所束缚。


②独立于 UI:前台展示的样式可能会随时发生变化(今天可能是网页、明天可能变成 console、后天是独立 app),但是底层架构不应该随之而变化。


③独立于底层数据源:无论今天你用 MySQL、Oracle 还是 MongoDB、CouchDB,甚至使用文件系统,软件架构不应该因为不同的底层数据储存方式而产生巨大改变。


④独立于外部依赖:无论外部依赖如何变更、升级,业务的核心逻辑不应该随之而大幅变化。


⑤可测试:无论外部依赖了什么数据库、硬件、UI 或者服务,业务的逻辑应该都能够快速被验证正确性。


作者:构即人生

编辑:陶家龙

出处:toutiao.com/i6903053083555807752/

图片

精彩文章推荐:

8年开发,登录接口写这么烂...
别再用kill -9关闭程序了......
Session不香吗,为什么还要Token?

时尚

  • 还在“保守”穿搭?看看45岁赵薇的堆堆靴,不要太时髦
    还在“保守”穿搭?看看45岁赵薇的堆堆靴,不要太时髦
    赵薇这次参加活动的服装造型走的是干练简约风,身穿粉色衬衣下搭棕色西裤亮相,粉红色的小格纹衬衣内搭白色短T,简约搭配很符合她的风格。她将长裤塞进长靴中,过膝长靴加持气场强大,不过这个穿法本以为会很土...
    要你穿得好看
  • 美成仙的夏季睡衣我都给你们整理好了!快来看!!
    美成仙的夏季睡衣我都给你们整理好了!快来看!!
    嗨,小可爱们早上好呀,我是颜九。 天气已经热起来了,之前阿九就跟舅舅们提到过,每天阿九最开心的事情,就是忙碌了一天之后回家泡澡。 洗完澡,穿上舒适的睡衣,窝在柔软的被子里煲剧,真的舒适到极点了。 阿...
    认真少女颜九
  • 抹胸吊带 清凉一夏
    抹胸吊带 清凉一夏
    抹胸吊带清凉一夏抹胸吊带或许是许多女孩子夏季不可缺少的单品不仅清凉舒适而且还能凸显锁骨和肩颈线条之美宋 茜一袭黑色吊带深V幽雅夜蝶裙,腰前的巨型蝴蝶结增添了几分童话氛围,黑发红唇,搭配珍珠配饰,灵...
    米娜
  • 黄金首饰免费换新的?还有能开出金条的黄金盲盒!乾昌珠宝这次玩的有点大~
    黄金首饰免费换新的?还有能开出金条的黄金盲盒!乾昌珠宝这次玩的有点大~
    ///6月7日—6月15日乾昌珠宝再次带着诚意回归! 6.7乾昌日,是乾昌珠宝一年一度给消费者的超级福利也是珠宝界前所未有的最强福利黄金免费以旧换新全场任意换购惊喜黄金盲盒听说还有金条大奖赶快来一探究竟吧!到...
    我和郑州
  • 炎炎夏日“降温色”必须GET!高级显白,谁穿谁美!这个夏天不容错过!
    炎炎夏日“降温色”必须GET!高级显白,谁穿谁美!这个夏天不容错过!
    服饰与搭配公众号ID:fusdapei关注天气越来越热了除了“能穿多少穿多少”之外还能有什么办法看上去清凉一点呢?在穿搭上选择一些“降温色”清凉值max,让人眼前一亮!“降温色”简单来说冰淇淋色低饱和度、低亮...
    服饰与搭配
  • 奶油黄、薄荷绿才是最适合夏天的颜色!太太太清爽了!
    奶油黄、薄荷绿才是最适合夏天的颜色!太太太清爽了!
    作者:itMode来源:itMode(ID:itMode)本文经授权发布我总觉得,身上不穿点彩色仿佛就是对夏天的背叛。最热烈的季节自然要穿上最美的颜色才算不辜负,比起鲜艳的大面积色彩,活力且温柔的两两配色不仅更有层次...
    美的你
  • 朱星杰演绎绝美中国风穿搭,分享男明星扮帅小秘密丨中国风格·时尚微综艺
    朱星杰演绎绝美中国风穿搭,分享男明星扮帅小秘密丨中国风格·时尚微综艺
    搜狐时尚【中国风格】品鉴大赏重磅策划之【时尚微综艺】邀请明星、时尚kol、模特同台用娱乐与时尚相结合的形式评测爆款国潮单品解码中国设计师们的奇思妙想【中国风格·时尚微综艺】首期节目邀请到了青年唱作人...
    搜狐时尚
  • 钻石!骗了全世界130年的骗局,这次彻底败给了中国制造!
    钻石!骗了全世界130年的骗局,这次彻底败给了中国制造!
    根据央视财经的报道,中科院“种”出了钻石——在实验室环境下,一星期即可培育一颗1克拉的钻石,其硬度和纯净度都可以媲美天然钻石,价格却只有天然钻石的六分之一!据介绍,“种”钻石的原料是天然钻石的籽金...
    玉器鉴定专家
  • 我的手表下海、泡温泉、进汤锅,但一点问题都没有
    我的手表下海、泡温泉、进汤锅,但一点问题都没有
    腕表防水的重要性我们已经和大家说过很多遍的,但总是不乏反对的声音,而且往往是现身说法。在我们曾经推送给大家的一篇关于腕表防水的文章中最近有表友这样留言:“我的手机(应该是手表)试过在海边游泳、泡温...
    腕表时代
  • 18套清爽又慵懒的穿搭,助你春夏换季不纠结!
    18套清爽又慵懒的穿搭,助你春夏换季不纠结!
    进入五月份,温度快速提升了很多,而这时候你要开始烦恼春夏换季怎么搭配了?春夏变时髦的关键,在于“清爽”二字,日常我们选择化繁为简的穿法,很轻松就能穿出日常约会都很养眼的look。而今天为大家分享的18套...
    美妆
  • T恤永远不嫌多!女明星同款都在这篇了!
    T恤永远不嫌多!女明星同款都在这篇了!
    每年一快到三伏天时,身边的朋友就跟说好了似的开始疯狂囤短袖。据她们说,这种差不多一年一换的单品也就穿个新,来年基本要么是不喜欢去年的款式了,要么就是衣服领子坏掉了不好再穿。其实仔细想想也对,貌似我...
    Lisa的美妆日记
  • YSL今年就靠这只包拉业绩了!还不赶紧入手?
    YSL今年就靠这只包拉业绩了!还不赶紧入手?
    说到YSL,你会想到什么?瑞酱猜,大部分人一定是先想到它家的口红!从圆管到方管再到小金条……可以说每个女孩的化妆包都是一部“YSL口红进化史”:但抛开口红,YSL的成衣和包包,在时尚圈的地位也是不可小觑的...
    瑞丽up
  • 在意大利,遇见真正的英伦公子
    在意大利,遇见真正的英伦公子
    “ 当英式优雅与意式激情相遇——预示着夏日之美正跃入生活之中,我们选择 Loro Piana 的 André 亚麻衬衫作为夏日衣橱永不过时的经典基础款。”试想一下这样的场景:在黎明的第一道霞光中醒来,在泳池边,以最...
    时尚先生
  • 腿粗女孩求求你们别再乱穿了!!
    腿粗女孩求求你们别再乱穿了!!
    我的小表妹们晚上好!夏天如何穿衣显瘦应该是你们近期最关注的话题了吧!毕竟露胳膊露腿的,每天都在想该咋遮遮这白花花的肉呢?cr:禾冉vy/陈吸吸本吸/葵花妙飞鱼像我自己就腿上的肉最多,谁懂粗腿女生的痛‍♀...
    时尚大表姐
  • 怎么穿衣服显得干净利落?跟着这样穿凸显独特气质!
    怎么穿衣服显得干净利落?跟着这样穿凸显独特气质!
    干净利落的穿衣搭配更容易让女人释放魅力,尤其是夏季,穿上简单时尚的衣服,能让我们更有自信和气质哦。下面为大家介绍的内容是怎么穿衣服显得干净利落,赶紧来看看吧!怎么穿衣服显得干净利落搭配1:系带无袖...
    爱靓时尚
1 2 3 4 5 >> 

公众号 • 51CTO技术栈

  • 51CTO技术栈微信号 : blog51cto
  • 51CTO技术栈专注于IT技术领域,汇聚技术大咖为您分享开发架构、系统运维、大数据、人工智能等一线技术解析和实践案例等深度干货文章,愿我们一起悦享技术,成就CTO梦想!
  • 手机微信扫描上方二维码进行订阅
Flag Counter