领域驱动设计应用之WebAPI

领域驱动设计应用之WebAPI

此篇文章主要讲述领域驱动设计在WebApi中的应用,以及设计方式,这种设计的原理以及有点。


文章目录

  • 领域驱动设计应用之WebAPI
  • 前言
  • 一、相对于传统设计模式的有点
  • 二、WebAPI对接中的使用案例
    • 业务拆分
    • 父类设计
      • HttpResponse(返回)
      • HttpRequest(请求)
      • Client(客户端发起请求方法)
    • 业务领域设计
      • HttpResponse(返回)
      • HttpRequest(请求)
    • 客户端设计(Client)
  • 总结


前言

随着技术的不断迭代升级,设计方式也在不断迭代更新,目前比较流行的就是领域驱动设计的方式来开发程序,领域驱动设计相对于传统设计模式的有点在于:1、更好地理解业务需求。2、更好的设计质量。3、更好的团队协作。4、更好的的业务创新。


一、相对于传统设计模式的有点

领减驱动设计(Domain-DrixenDesig,简称DDD)是一种软件开发方法论,旨在解决复杂业务场景下的软件设计问
题。DDD的核心理念是将业务领域作为重点,将软件系统分解为多个子域,并通过领域模型、聚合、实体等概念来描述和
实现领域逻辑。
领域驱动设计的好处主要包括以下几点:
1.更好地理解业务需求:DDD强调将业务领域作为设计的重点,通过深入了解业务需求和领域知识,能够更好地把握业务流程和逻辑,从而更好地满足用户需求。
2.更好的设计质量:DDD通过建立领域模型、聚合和实体等概念,能够更好地划分系统结构和职责,提高系统的可维护性、可扩展性和可测试性。同时,领域模型也能够更好地反映业务实体之间的关系和行为,提高系统的质量和稳定性。
3.更好的团队协作:DDD强调跨部门、跨角色的协作,通过建立共同的领域模型和语言,能夠够更好地促进团队之间的
沟通和协作,提高团队的效率和协作能力。
4.更好的业务创新:DDD强调将业务领域作为重点,通过深入理解业务需求和领域知识,能够更好地发掘业务创新
点,提高业务竞争力。
总之,领域驱动设计可以帮助开发团队更好地理解业务需求、提高设计质量、促进团队协作和推动业务创新。

二、WebAPI对接中的使用案例

业务拆分

众所周知,对接结果的操作可分为请求(HttpRequest)和返回(HttpResponse)两个大类的操作
那么我们就根据这两个操作来进行业务拆分。
项目结构展示:
在这里插入图片描述

父类设计

HttpResponse(返回)

HttpResponse的父类 也就是项目结构中的IBaseRes的设计方式

代码如下:

 public interface IBaseRes{/// <summary>/// 请求码/// </summary>int Code { get; set; }/// <summary>/// 文本信息/// </summary>string Msg { get; set; }/// <summary>/// 返回时间/// </summary>string Time { get; set; }}public abstract class BaseRes : IBaseRes{public BaseRes(){this.Code = 1;this.Msg = "接收返回内容失败";this.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");}/// <summary>/// 请求码/// </summary>[JsonProperty("code")]public int Code { get; set; }/// <summary>/// 文本信息/// </summary>[JsonProperty("msg")]public string Msg { get; set; }/// <summary>/// 返回时间/// </summary>[JsonProperty("time")]public string Time { get; set; }}public static class Serialize{public static string ToJson(this GoodsSyncStockReq self) => JsonConvert.SerializeObject(self, Converter.Settings);}internal static class Converter{public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings{MetadataPropertyHandling = MetadataPropertyHandling.Ignore,DateParseHandling = DateParseHandling.None,Converters ={new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }},};}

这里主要是提取所有接口返回的共用字段,减少代码的冗余
以上代码可以看出,我们先设计了个接口,在接口中规定了有那些共用字段,在下方又实现了接口并使用Newtonsoft序列化工具特性规定其在返回时的字段名称。

HttpRequest(请求)

HttpRequest的父类 也就是项目结构中的BaseReq的设计方式

代码如下(示例):

  public interface IBaseReq<T> where T : IBaseRes{/// <summary>/// 获取接口路径/// </summary>/// <returns></returns>string GetApi();}

这里主要是限制类型必须是IBaseRes(返回基类)的派生类,并给出获取接口名称的方法

Client(客户端发起请求方法)

public interface IPingAnClient{/// <summary>/// 执行请求/// </summary>/// <typeparam name="T">领域对象</typeparam>/// <param name="request">请求数据</param>/// <returns></returns>Task<T> Send<T>(IBaseReq<T> request) where T : BaseRes;}

这里也是以接口的方式实现,给出了一个Send方法,该方法是用于发起Http请求的方法。

业务领域设计

以下给出的是业务领域模型设计的代码以及设计方式和原理

HttpResponse(返回)

代码如下:

 public  class GoodsListsRes :BaseRes{[JsonProperty("data")]public GoodsListInf Data { get; set; }}

以上代码可以看出这里的业务领域模型继承了(BaseRes)返回基类


有的爱思考的朋友就会提问这里为什么data会在这里呢?

这里是因为每个领域返回的data内容的结构是不样的所有,就将他下放至每个领域中各自实现。


HttpRequest(请求)

代码如下(示例):

 public class GoodsListsReq : IBaseReq<GoodsListsRes>{/// <summary>/// 商品类型 1销售中 2仓库中[可选]/// </summary>public int type { get; set; }/// <summary>/// 返回接口请求路径/// </summary>public string GetApi(){return "/goods/lists";}}

以上代码可以看出这里的业务领域模型继承了(IBaseReq)请求基类


眼尖的同学相必已经发现了这里我们传了一个GoodsListsRes那这又是为什么?
这里是为了方便我们后面返回时直接反射创建对象接收返回值。


客户端设计(Client)

 public class DefaultPingAnClient : IPingAnClient{private string serverUrl;private string appKey;private string appSecret;public DefaultPingAnClient(string serverUrl, string appKey, string appSecret){this.appKey = appKey;this.appSecret = appSecret;this.serverUrl = serverUrl;}/// <summary>/// 发起请求/// </summary>/// <typeparam name="T">领域对象</typeparam>/// <param name="request">请求值</param>/// <returns></returns>public async Task<T> Send<T>(IBaseReq<T> request) where T : BaseRes{return await DoSend(request);}private async Task<T> DoSend<T>(IBaseReq<T> request) where T : BaseRes{return await Response(request, request.GetApi());}#region 请求相关/// <summary>/// 签名/// </summary>/// <param name="dir">请求参数</param>/// <returns></returns>private string Sign(Dictionary<string, object> dir){string sign = string.Empty;if (dir.Count() > 0){//拼接请求参数,如果不存在不用拼接,拼接前要进行排序(a=1&b=2&c=3)dir = dir.OrderBy(x => x.Key).ToDictionary(f => f.Key, f => f.Value);sign = JsonConvert.SerializeObject(dir);}DateTimeOffset now = DateTimeOffset.Now;//拼接unix时间戳long unixTimestamp = (long)(now - new DateTime(1970, 1, 1)).TotalSeconds;sign += unixTimestamp;sign += appSecret;sign = Md5(sign).ToString();return sign.ToLower();}/// <summary>/// 发起请求/// </summary>/// <param name="Url">请求路径</param>/// <returns></returns>private async Task<T> Response<T>(IBaseReq<T> request, string Url, ResponseType RType = ResponseType.Post) where T : BaseRes{var rspModel = Activator.CreateInstance<T>();try{var url = serverUrl + Url;Dictionary<string, object> parames = new Dictionary<string, object>();if (request != null){parames = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject(request)) ?? new Dictionary<string, object>();}using (var client = new HttpClient()){client.DefaultRequestHeaders.Add("sign", Sign(parames));client.DefaultRequestHeaders.Add("timestamp", timeStamp().ToString());client.DefaultRequestHeaders.Add("key", appKey);client.Timeout = TimeSpan.FromSeconds(30);HttpResponseMessage response = new HttpResponseMessage();switch (RType){case ResponseType.Post:response = client.PostAsync(url, new StringContent(JsonConvert.SerializeObject(parames), Encoding.UTF8, "application/json")).Result;break;case ResponseType.Put:response = client.PutAsync(url, new StringContent(JsonConvert.SerializeObject(parames), Encoding.UTF8, "application/json")).Result;break;case ResponseType.Delete:response = client.DeleteAsync(url + "?" + buildParamStr(parames)).Result;break;case ResponseType.Get:response = client.GetAsync(url + "?" + buildParamStr(parames)).Result;break;default:rspModel.Code = 1;rspModel.Msg = "请求类型错误!";break;}if (response != null && response.IsSuccessStatusCode){string responseText = response.Content.ReadAsStringAsync().Result;if (!string.IsNullOrWhiteSpace(responseText)){if (responseText.Contains("签名")){SginErroRes erroRes = JsonConvert.DeserializeObject<SginErroRes>(responseText);rspModel.Msg = erroRes.Msg;rspModel.Time=erroRes.Time;}else{rspModel = JsonConvert.DeserializeObject<T>(responseText);}}else{rspModel.Msg = "返回内容为空!";}}};}catch (Exception ex){rspModel.Code = 1;rspModel.Msg = ex.Message;}return rspModel;}/// <summary>/// 请求类型/// </summary>private enum ResponseType{Post,Put,Delete,Get,}/// <summary>/// 拼接请求参数/// </summary>/// <param name="param">参数字典</param>/// <returns></returns>private static String buildParamStr(Dictionary<string, object> param){String paramStr = String.Empty;if (param != null){foreach (var key in param.Keys.ToList()){string keyparam = param[key].ToString().Replace("+", "%2B");//keyparam = HttpUtility.UrlEncode(keyparam);if (param.Keys.ToList().IndexOf(key) == 0){paramStr += (key + "=" + keyparam);}else{paramStr += ("&" + key + "=" + keyparam);}}}return paramStr;}/// <summary>/// Md5加密/// </summary>/// <param name="str"></param>/// <returns></returns>private string Md5(string str){if (string.IsNullOrEmpty(str)) str = "";MD5 md5Hash = MD5.Create();byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(str));StringBuilder sBuilder = new StringBuilder();for (int i = 0; i < data.Length; i++){sBuilder.Append(data[i].ToString("x2"));}return sBuilder.ToString();}/// <summary>/// 秒级时间戳/// </summary>/// <returns></returns>private long timeStamp(){return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;}#endregion}

这里需要着重讲解一下 Response 方法:
方法里使用到了:Activator.CreateInstance,微软文档是这样说的 传送门

其实我的理解就是利用反射创建对应对象,这里可能会返回null
还用到了:HttpClient 微软文档是这样说的 传送门


总结

在这里插入图片描述
从这里就可以体现出领域驱动设计的效果了,商品领域的请求和返回只会用到它自己领域的东西。这也很好的避免了传统设计方式会出现的代码冗余问题。也可以方便后期维护,需要修改那个领域就可以很快的找到相关的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/620583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024PMP考试新考纲-【过程领域】近期典型真题和很详细解析(5)

今天华研荟继续为您分享【过程Process领域】的新考纲下的真题&#xff0c;进一步帮助大家体会和理解新考纲下PMP的考试特点和如何应用知识来解题&#xff0c;并且举一反三&#xff0c;在两个多月的时间内&#xff0c;一次性、高等级通过2024年PMP考试。 2024年PMP考试新考纲-【…

HBase 复制、备份、迁移

行业分享 HBase金融大数据乾坤大挪移 https://www.jianshu.com/p/cb4a645dd66a HBase跨机房迁移技术分享总结 https://www.jianshu.com/p/defc787b2704 dbaplus181期&#xff1a;腾讯金融HBase跨机房迁移实战 https://m.qlchat.com/topic/details?topicId2000003847589595 ht…

C++学习笔记——友元、嵌套类、异常

目录 一、友元 一个使用友元的示例代码 输出结果 二、嵌套类 一个使用嵌套类的示例代码 输出结果 三、异常 一个使用异常处理的示例代码 输出结果 四、结论 五、使用它们的注意事项 上一篇文章链接&#xff1a; C中的继承和模板是非常强大和灵活的特性&#xff0c;它…

【HuggingFace Transformer库学习笔记】基础组件学习:Datasets

基础组件——Datasets datasets基本使用 导入包 from datasets import *加载数据 datasets load_dataset("madao33/new-title-chinese") datasetsDatasetDict({train: Dataset({features: [title, content],num_rows: 5850})validation: Dataset({features: [titl…

【图形学】探秘图形学奥秘:DDA与Bresenham算法的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《图形学 | 图像解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f30c;1. 初识模式识别…

VMware workstation安装debian-12.1.0虚拟机并配置网络

VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统&#xff01;Debian 有一个由普罗大众组成的社区&#xff01;该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…

Redis命令 - Strings命令组常用命令

1、Set命令 SET key value [EX seconds] [PX milliseconds] [NX|XX]1.1 参数说明&#xff1a; EX seconds: 设置key的过期时间&#xff0c;单位时秒PX milliseconds: 设置key的过期时间&#xff0c;单位时毫秒NX: 只有key不存在的时候&#xff0c;才会设置key的值XX: 只有key…

花了三天的时间做了一个多功能 AI 助手

嗨&#xff01;我是团子&#xff0c;大家新年快乐呀~ 前几天看到一些好朋友在朋友圈晒自己的年度总结&#xff0c;立新年 Flag&#xff0c;看到大家一年满满的收获&#xff0c;再看看自己&#xff0c;不由得想再看看人家&#xff0c;然后再看看自己&#xff0c;然后再看看人家…

MAVROS的进一步理解

一、Mavros简介 顾名思义&#xff0c; mavros就是mavlinkros。mavros是PX4官方提供的一个运行于ros下收发mavlink消息的工具&#xff0c;利用mavros可以发送mavlink消息给飞控(可以控制飞机)&#xff0c;并且可以从飞控中接受数据(例如&#xff1a;飞控的位置速度 IMU数据等等…

阿里云服务器ECS介绍_高性能云服务器_为了无法计算的价值

阿里云高性能云服务器60%单实例最大性能提升&#xff0c;35Gbps内网带宽&#xff0c;网络增强&通用型云服务器、本地SSD型云服务器、大数据型云服务器、GPU异构型云服务器&#xff0c;阿里云百科aliyunbaike.com分享阿里云高性能云服务器&#xff1a; 阿里云高性能云服务器…

大模型实战营Day4 作业

基础作业&#xff1a; 构建数据集&#xff0c;使用 XTuner 微调 InternLM-Chat-7B 模型, 让模型学习到它是你的智能小助手&#xff0c;效果如下图所示&#xff0c;本作业训练出来的模型的输出需要将不要葱姜蒜大佬替换成自己名字或昵称&#xff01; 微调前&#xff08;回答比较…

LMDeploy 的量化和部署

LMDeploy 的量化和部署 文档&#xff1a;https://github.com/InternLM/tutorial/blob/vansin-patch-4/lmdeploy/lmdeploy.md 视频&#xff1a;https://www.bilibili.com/video/BV1iW4y1A77P 一、模型量化 大模型参数量很大&#xff0c;运行起来非常消耗显存和内存&#xff0c;…

[NSSCTF Round#16 Basic] CPR

打着玩玩&#xff0c;比赛很简单。 Crypto pr 一个RSA题&#xff0c;n1p*q,n2q*r给了两个c和p,r而且flag经过pad用单因子无法解出。分别用p,r解完再取crt from Crypto.Util.number import * import randomflagplaintext NSSCTF{****************} charset abcdefghijklmn…

MySQL基础学习: 使用EXPLAIN查看执行计划详解分析

一、EXPLAIN语句的作用 在客户端执行MySQL的操作语句&#xff0c;会依次经过MySQL客户端连接管理、语法解析与优化&#xff08;查询缓存、语法解析、查询优化&#xff09;、存储引擎层。其中查询优化器在基于成本和规则对查询语句进行优化&#xff0c;并且在优化后会生成一个执…

nacos配置中心只能获取部分配置的问题

检查配置中心&#xff0c;在配置中心里是可以看到监听的服务地址的&#xff0c;但是却获取不到配置 nacos配置中心主要是在这个NacosConfigService的这个类下面。该接口下面主要有一些获取配置&#xff0c;发布配置&#xff0c;增加监听器&#xff0c;删除配置&#xff0c;删…

一个简易的PHP论坛系统

一个简易的PHP论坛系统 php课程设计&#xff0c;毕业设计 预览 技术 bootstrap 4.x jquery css php mysql 5.7 目录结构 登录 管理员 admin/123456 测试用户 user1/123456 更多文章和源码获取查看

Windows平台RTMP推送|轻量级RTSP服务录像模块如何支持中文路径?

技术背景 我们在做Windows平台RTMP推送、轻量级RTSP服务录像模块的时候&#xff0c;部分开发者抱怨路径无法设置中文&#xff0c;只能设置为英文。 以C#的接口为例&#xff0c;早期的设计如下&#xff1a; /** 设置本地录像目录, 必须是英文目录&#xff0c;否则会失败*/[DllI…

【Java SE语法篇】8.面向对象三大特征——封装、继承和多态

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 1. 封装1.1 封装的概念1.2 为什么封装1.3 封装的实现…

Leetcode with Golang 滑动窗口 Part1

滑动窗口的定义&#xff1a; 滑动窗口这一个技巧主要运用于处理数组问题上&#xff0c;一般用于“子串”问题。精髓是&#xff0c;维护一个里面装着元素的“窗口”&#xff0c;在将新元素装进“窗口”的同时&#xff0c;根据题意&#xff0c;把不符合题意的元素踢出“窗口”。…

漏洞复现-nginxWebUI runCmd前台远程命令执行漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…