diff --git "a/\351\202\271\347\246\217\347\247\221/20240708-DDD\346\216\242\350\256\250\346\200\235\350\200\203.md" "b/\351\202\271\347\246\217\347\247\221/20240708-DDD\346\216\242\350\256\250\346\200\235\350\200\203.md" new file mode 100644 index 0000000000000000000000000000000000000000..eca5aa35ccb7b2b1443fb71c6a1cd8d3faaeff10 --- /dev/null +++ "b/\351\202\271\347\246\217\347\247\221/20240708-DDD\346\216\242\350\256\250\346\200\235\350\200\203.md" @@ -0,0 +1,43 @@ +## 关于DDD领域驱动设计思想的探讨与思考 + +1. DDD中一些概念 + + - 域 + + - 子域 + + - 聚合 + + - 聚合根 + + - 领域模型 + + - 值对象 + + - 通用仓储接口 (底层 贴近数据) + + - 领域模型仓储接口 + + - 领域服务接口 + + - 应用服务接口 + + - API服务接口 (高层,因为贴近因为) + + - 领域事件 + + - 大脑风暴 + + - 事件风暴 + +领域模型:一些不涉及其它对象(不是指其他类型,是指对象实例)的业务操作,放在领域模型 + +1. 通用仓储接口: 通用的仓储接口,其实现放在基础设施层、单独的一层ORM工具层 + +2. 领域模型仓储接口:领域独有的一些业务,可以写成领域模型接口,其实现放在Domain + +3. 领域服务接口:涉及到多个领域对象的业务和操作,放在领域服务;但是持久化也放在领域服务层 + +4. 应用服务接口:应用服务其实就是对领域模型业务和领域服务业务进行编排的一个层,比较薄 + +5. API服务接口 (高层,因为贴近因为):接受参数,最多做一些数据验证工作 diff --git "a/\351\202\271\347\246\217\347\247\221/20240709-\351\233\252\350\212\261\347\256\227\346\263\225.md" "b/\351\202\271\347\246\217\347\247\221/20240709-\351\233\252\350\212\261\347\256\227\346\263\225.md" new file mode 100644 index 0000000000000000000000000000000000000000..66591fe98fb9c9b52257eaeb8c7efedd97b7475c --- /dev/null +++ "b/\351\202\271\347\246\217\347\247\221/20240709-\351\233\252\350\212\261\347\256\227\346\263\225.md" @@ -0,0 +1,26 @@ +## 一、生成id的几种方法 +### 1.UUID +- UUID由于是在本地生成,因此具有极高的性能,然而,它生成的ID长度较长,达到16字节128位,通常需要使用字符串类型进行存储 +- UUID是无序的,其无序性可能会导致数据位置频繁变动,从而严重影响性能 + +### 2.数据库自增ID方式 +- 每次获取ID都需要进行数据库IO操作,给数据库带来较大压力,且性能较低 +- 创建具有可预测性,可能导致信息泄露风险 +- 一旦分配的ID被删除,那么这个ID就无法被再次使用,即使后续有新的记录需要插入,这可能导致ID空间的浪费 +- 可能由于ID冲突,不利于数据迁移和合并,特别是当合并的两个系统中存在重复的ID + +### 3.雪花算法 +- 以时间戳、机器标识和递增序列为基础生成ID,呈趋势递增,且性能很高 +- 算法的实现相对简单,不依赖于数据库等外部存储 +- Snowflake算法适用于分布式系统,生成的ID不依赖于中心化的存储或管理,每个节点可以独立生成ID而不会发生冲突 +- 由于它强烈依赖于机器时钟,因此需要考虑时钟回拨问题。即当机器上的时间因校正而发生倒退时,可能会导致生成的ID重复 + +## 二、雪花算法的定义 +1. 41位时间戳:这部分能够表示的时间跨度为(1L<<41)/(1000L*3600*24*365),即大约69年 +2. 10位机器ID:可以唯一标识最多1024台机器,如果需要对互联网数据中心(IDC)进行划分,可以将这10位拆分为两部分,例如各5位,这样,系统就能够表示最多32个IDC,且每个IDC下可以容纳32台机器 +3. 2位自增序列号:用于在同一毫秒内生成多个ID,最多可以表示2^12个不同的ID,因此,理论上,Snowflake算法能够达到的每秒查询率(QPS)约为409.6万 + + +## 三、代码实现snowflake +具体见: +[雪花算法详解](https://blog.csdn.net/prjh_/article/details/134654579) \ No newline at end of file diff --git "a/\351\202\271\347\246\217\347\247\221/20240710-ABP.md" "b/\351\202\271\347\246\217\347\247\221/20240710-ABP.md" new file mode 100644 index 0000000000000000000000000000000000000000..4b5e48fc2aae1e82b7d1f7d088ce51080837846b --- /dev/null +++ "b/\351\202\271\347\246\217\347\247\221/20240710-ABP.md" @@ -0,0 +1,62 @@ +### 一、什么是ABP +- ABP(ASP .NET Boilerplate Project)是基于DDD的经典分层架构思想 +- 基于领域驱动的理念来构架整个架构,其中领域驱动包含的概念有: + - 域 + - 子域 + - 聚合 + - 聚合根 + - 领域模型 + - 值对象 + - 通用仓储接口(底层:贴近数据库) + - 领域模型仓储接口 + - 领域服务接口 + - 应用服务接口 + - API服务接口(高层:贴近用户) + - 领域事件 + - 大脑风暴 + - 事件风暴 + *领域模型:一些不涉及其他对象(不是指其他类型,是指对象实例)的业务操作,放在领域模型(即Domain)* +- 具体层分析 + 1. 通用仓储接口:通用的仓储接口,其实现放在基础设施层、单独的一层ORM工具层 + 2. 领域模型仓储接口:领域独有的一些业务,可以写成领域模型接口,其实现放在Domain + 3. 领域服务接口:涉及到多个领域实体的业务和操作,放在领域服务 + 4. 应用服务接口:应用服务其实就是对领域模型业务和领域服务业务进行编排的一个层,比较薄 + 5. API服务接口:接受参数,最多做一些数据验证工作 + 6. 单元测试层Tests:提供应用层对象的模拟测试,其中测试的数据库使用的是EntityFramework的内存数据库,不影响实际数据库内容 + +### 二、采用的技术 +#### 服务端 +- ASP.NET MVC5、Web API2、C# +- DDD领域驱动设计 +- Castle windsor(依赖注入容器) +- Entity Framework6\EntityFrameworkCore\NHibernate、数据迁移 +- Log4Net(日志记录) +- AutoMapper(实现DTO类与实体类的双向自动转换) + +#### 客户端 +- Bootstrap +- Less +- Angular +- Vue +- jQuery +- Modernizr +- 其他JS库 + + +### 三、ABP框架的特点 +1. 依赖注入:这个部分使用Castle来实现依赖注入 +2. Repository仓储模式 +3. 身份验证与授权管理 +4. 数据有效性验证 +5. 审计日志记录 +6. Unit Of Work工作单元模式 +7. 异常处理 +8. 日志记录 +9. 多语言/本地化支持 +10. AutoMapping自动映射:实现领域对象和DTo对象的属性映射 +11. 动态WebAPI层 +12. 多租户支持 +13. 软删除支持 +14. 系统设置存取管理(系统级、租户级、用户级,作用范围自动管理) +15. 领域事件 +16. 模块以及模块的依赖关系实现插件化的模块处理 diff --git "a/\351\202\271\347\246\217\347\247\221/20240711-\346\265\213\350\257\225.md" "b/\351\202\271\347\246\217\347\247\221/20240711-\346\265\213\350\257\225.md" new file mode 100644 index 0000000000000000000000000000000000000000..b3ccb5f25419a0a0a3299820aedb32e0431bd7e7 --- /dev/null +++ "b/\351\202\271\347\246\217\347\247\221/20240711-\346\265\213\350\257\225.md" @@ -0,0 +1,73 @@ +## 一、什么是测试 +- 测试是软件生命周期中一个非常重要的阶段,在应用程序的开发过程中,为了确保它的功能与预期一致,必须对其进行测试 +- 帮助开发员改正系统中所存在的缺陷,提高软件的可靠性 +- 测试应该覆盖到软件的所有功能,全面、细致的测试会在很大程度上节省软件开发的成本 + +## 二、测试的类型 +根据不同的维度,可以把测试方法分为不同的类型 +- 从结构的透明性方式,分为白盒测试、黑盒测试和灰盒测试 + - 白盒测试(结构测试):在已知程序的内部逻辑前提下,对程序内部结构进行验证 + - 黑盒测试(功能测试):将软件视为“黑盒”,在对程序内部实现未知的前提下,按照需求和预期结果对程序的功能进行测试 + - 灰盒测试:介于二者之间,在已知程序内部逻辑的前提下,对软件进行功能测试 +- 从测试执行方式,分为手工测试和自动化测试 + - 手工测试:要求测试人员与最终使用软件的用户一样,对软件功能进行实际操作并验证 + - 自动化测试:通过专业的测试软件、测试脚本騞自动化测试用例对软件进行测试 +- 从测试所涉及的层次,分为单元测试、集成测试和系统测试 + - 单元测试:验证代码段(如方法或函数)功能的测试,通常由开发人员编写相应的测试方法,以验证代码执行后与预期结果是否一致 + - 集成测试:用于验证具有依赖关系的多个模块或组件是否能够正常工作 + - 系统测试:对整个系统进行全面测试 + +## 三、单元测试层 +### 1.概念 +- 是指对软件中的最小可测试单元进行检查和验证,有助于确保领域模型的各个部分在随着时间推移和代码更改时保持稳定和正确性 +- 遵循Arrange-Act-Assert模式: + - Arrange:为测试进行准备操作,如设置测试数据 + - Act:执行要测试的方法,如调用要测试的函数和方法 + - Assert:断言测试结果,验证被测试方法的输出是否与预期的结果一样 +### 2.怎么写 +步骤: +1. 选择合适的单元:确定要测试的领域模型单元,如实体、值对象、聚合根等等 +2. 测试覆盖:确保测试覆盖所有重要的业务逻辑和边界情况 +3. 独立性:确保测试是独立于外部依赖的,例如数据库、网络服务等 +4. 清晰的测试名称:使用清晰而描述性的名称来命名测试 +5. 测试数据的准备和清理:在测试运行之前准备必要的测试数据,并且在测试结束后进行数据清理,以保证测试的一致性和独立性 +6. 持续集成和自动化:将单元测试整合到持续集成过程中,并自动运行以确保在代码更改时及时发现问题 +~~~C# +// 示例:测试添加用户是否成功 +// 假设有一个名为UserService的服务类负责用户管理(增删改查) +public class UserService +{ + privite List users = new List(); + public void AddUser(User newUser) + { + user.Add(newUser); + } + public bool UserExists(string username) + { + retrun users.Any(x=>x.Username == username); + } +} +// 测试文件 +[TestFixture] +public class UserServiceTests +{ + [Test] + public void AddUser_ShouldAddNewUser() + { + // 准备测试数据 + UserService userService = new UserService(); + User newUser = new User("abc","admin"); + + // 执行添加用户操作 + userService.AddUser(newUser); + + // 验证用户是否成功添加 + Assert.IsTrue(userServer.UserExixsts("admin")); + } +} +~~~ + +## 四、集成测试 +### 1.概念 +- 能够确保应用程序的组件正常工作,包括应用程序支持的基础结构,如数据库和文件系统等 +- 需要安装Microsoft.AspNetCore.Mvc.Testing包 \ No newline at end of file diff --git "a/\351\202\271\347\246\217\347\247\221/20240712-Antdv.md" "b/\351\202\271\347\246\217\347\247\221/20240712-Antdv.md" new file mode 100644 index 0000000000000000000000000000000000000000..af5ebbd50d33cea55c04ec252650979b4eacc8e8 --- /dev/null +++ "b/\351\202\271\347\246\217\347\247\221/20240712-Antdv.md" @@ -0,0 +1,65 @@ +## 一、引入 ant-design-vue +### 1.新建项目 +### 2.使用组件 +#### 1. 安装 +```js +npm i --save ant-design-vue@4.x +``` +#### 2. 注册 +1. 全局完整注册 +```js + import { createApp } from 'vue'; + import App from './App'; + // 以下完整引入antdv + import Antd from 'ant-design-vue'; + import 'ant-design-vue/dist/reset.css'; + + let app = createApp(App); + + app.use(Antd).mount('#app'); + // 样式文件需要单独引入 +``` +2. 全局部分注册 +```js + import { createApp } from 'vue'; + import { Button, message } from 'ant-design-vue'; + import App from './App'; + + let app = createApp(App); + + /* 会自动注册 Button 下的子组件, 例如 Button.Group */ + app.use(Button).mount('#app'); + + app.config.globalProperties.$message = message; +``` +3. 局部组件 +需要分别注册组件子组件,如 Button、ButtonGroup,并且注册后仅在当前组件中有效 +```js + // 选项式写法 + + + // 组合式写法 + + + +``` + +## 二、按需加载 +ant-design-vue 默认支持基于 ES modules 的 tree shaking,直接引入 import { Button } from 'ant-design-vue'; 就会有按需加载的效果 \ No newline at end of file