zoukankan      html  css  js  c++  java
  • CleanArchitecture原则CQRS模式

    一个遵循CleanArchitecture原则的Asp.net core轻量级开源项目

     

    这是一个基于最新的ASP.net core 5.0创建Razor Page应用程序解决方案模板。遵循Clean Architecture的原则,以最求简洁的代码风格和实现快速开发小型的web业务系统的目标,并且从没停止过更新。该项目从最早的asp.net web form,asp.net mvc5 到 asp.net core 3.1再到现在最新的asp.net core 5.0 Razor Page,从简单三层结构到N层结构再到现在流行的CQRS模式,一遍一遍的再重构,在这过程中体会到系统架构的重要性和在优秀的框架下开发系统是一件多么愉快的事情。做这样的项目纯粹是为了兴趣和能和很多Github上优秀的程序员一起交流和学习。
    image.png

    介绍

    Technologies

    特点

    项目结构

    image.png

    基本功能预览

    image.png

    • 新增
    • 修改
    • 删除
    • 查询
    • 导入Excel
    • 下载模板
    • 导出Excel

    用户管理

    image.png

    • 新增
    • 修改
    • 删除
    • 查询
    • 导入Excel
    • 下载模板
    • 导出Excel
    • 重置密码
    • 角色管理

    角色管理

    image.png

    • 新增
    • 修改
    • 删除
    • 查询
    • 导入Excel
    • 下载模板
    • 导出Excel
    • 授权管理

    如何开始

    1. 在Domain project中新增一个Entity,比如Customer客户信息
     public partial class Customer : AuditableEntity, IHasDomainEvent
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string NameOfEnglish { get; set; }
            public string GroupName { get; set; }
            public PartnerType PartnerType { get; set; }
            public string Region { get; set; }
            public string Sales { get; set; }
            public string RegionSalesDirector { get; set; }
            public string Address { get; set; }
            public string AddressOfEnglish { get; set; }
            public string Contract { get; set; }
            public string Email { get; set; }
            public string PhoneNumber { get; set; }
            public string Fax { get; set; }
            public string Comments { get; set; }
            public List<DomainEvent> DomainEvents { get; set; } = new();
        }
    
    1. 在Application project中实现具体的功能请遵循CQRS模式
      image.png
    • Command
      • AddEdit
      • Delete
      • Import
    • DTOs
    • Eventhandlers
    • Queries
      • Export
      • PaginationQuery
    1. 在SmartAdmin.WebUI中添加UI页面
    @page
    @using CleanArchitecture.Razor.Domain.Enums
    @using CleanArchitecture.Razor.Infrastructure.Constants.Permission
    @model SmartAdmin.WebUI.Pages.Customers.IndexModel
    @inject Microsoft.Extensions.Localization.IStringLocalizer<IndexModel> _localizer
    @inject Microsoft.AspNetCore.Authorization.IAuthorizationService _authorizationService
    @{
      ViewData["Title"] = _localizer["Customers"].Value;
      ViewData["PageName"] = "customers_index";
      ViewData["Category1"] = _localizer["Customers"].Value;
      ViewData["Heading"] = _localizer["Customers"].Value;
      ViewData["PageDescription"] = _localizer["See all available options"].Value;
      ViewData["PreemptiveClass"] = "Default";
      var _canCreate = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Create);
      var _canEdit = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Edit);
      var _canDelete = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Delete);
      var _canSearch = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Search);
      var _canImport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Import);
      var _canExport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Export);
    
    }
    @section HeadBlock {
    
        <link rel="stylesheet" media="screen, print" href="~/css/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.css">
        <link rel="stylesheet" media="screen, print" href="~/css/fa-solid.css">
        <link rel="stylesheet" media="screen, print" href="~/css/theme-demo.css">
        <link rel="stylesheet" media="screen,print" href="~/lib/easyui/themes/insdep/easyui.css">
        <style>
    
            .customer_dg_datagrid-cell-c1-_action {
                overflow: visible !important
            }
        </style>
    }
    <div id="js-page-content-demopanels" class="card mb-g">
        <div class="card-header bg-white d-flex align-items-center">
            <h4 class="m-0">
                @_localizer["Customers"] 
                <small>@_localizer["See all available options"]</small>
            </h4>
            <div class="ml-auto">
                @if (_canCreate.Succeeded)
                {
                    <button class="btn btn-sm btn-outline-primary " id="addbutton">
                        <span class="@(Settings.Theme.IconPrefix) fa-plus mr-1"></span>
                        @_localizer["Add"]
                    </button>
                }
                @if (_canDelete.Succeeded)
                {
                    <button class="btn btn-sm btn-outline-danger" disabled id="deletebutton">
                        <span class="@(Settings.Theme.IconPrefix) fa-trash-alt mr-1"></span>
                        @_localizer["Delete"]
                    </button>
                }
                @if (_canSearch.Succeeded)
                {
                    <button class="btn btn-sm btn-outline-primary " id="searchbutton">
                        <span class="@(Settings.Theme.IconPrefix) fa-search mr-1"></span>
                        @_localizer["Search"]
                    </button>
                }
                @if (_canImport.Succeeded)
                {
                    <div class="btn-group" role="group">
                        <button id="importbutton" type="button" class="btn btn-sm  btn-outline-primary waves-effect waves-themed">
                            <span class="@(Settings.Theme.IconPrefix) fa-upload mr-1"></span>   @_localizer["Import Excel"]
                        </button>
                        <button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle dropdown-toggle-split waves-effect waves-themed" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            <span class="sr-only">Toggle Dropdown</span>
                        </button>
                        <div class="dropdown-menu" aria-labelledby="importbutton">
                            <button id="gettemplatebutton" class="dropdown-item">@_localizer["Download Template"]</button>
                        </div>
                    </div>
                }
                @if (_canExport.Succeeded)
                {
                    <button class="btn btn-sm btn-outline-primary " id="exportbutton">
                        <span class="@(Settings.Theme.IconPrefix) fa-download mr-1"></span>
                        @_localizer["Export Excel"]
                    </button>
                }
                </div>
        </div>
        <div class="card-body">
            <div class="row">
                <div class="col-md-12">
                    <table id="customer_dg">
                    </table>
                </div>
            </div>
        </div>
    </div>
    <div class="modal fade" id="customer_modal" tabindex="-1" role="dialog" aria-hidden="true">
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">Modal title</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true"><i class="@(Settings.Theme.IconPrefix) fa-times"></i></span>
                    </button>
                </div>
                <form id="customer_form" class="needs-validation" novalidate="novalidate">
                    ...
                </form>
            </div>
        </div>
    </div>
    @await Component.InvokeAsync("ImportExcel", new { importUri = Url.Page("/Customers/Index") + "?handler=Import",
        getTemplateUri = @Url.Page("/Customers/Index") + "?handler=CreateTemplate",
        onImportedSucceeded = "reload()" })
    @section ScriptsBlock {
        <partial name="_ValidationScriptsPartial" />
    
        <script type="text/javascript" src="~/lib/easyui/jquery.easyui.min.js" asp-append-version="true"></script>
        <script type="text/javascript" src="~/lib/easyui/jquery.easyui.component.js" asp-append-version="true"></script>
        <script type="text/javascript" src="~/lib/easyui/plugins/datagrid-filter.js" asp-append-version="true"></script>
        <script>jQuery.fn.tooltip = bootstrapTooltip;</script>
        <script src="~/lib/axios/dist/axios.js"></script>
        <script src="~/lib/jquery-form/jquery.jsonToForm.js"></script>
    
        <script type="text/javascript">
            $('#searchbutton').click(function () {
                reload();
            });
            $('#addbutton').click(function () {
                popupmodal(null);
            });
            $('#deletebutton').click(function () {
                onDeleteChecked();
            });
            $('#exportbutton').click(function () {
                onExport();
            });
            $('#importbutton').click(function () {
                showImportModal();
            });
            $('#gettemplatebutton').click(function () {
                onGetTemplate();
            });
            $('#customer_form :submit').click(function (e) {
                ...
                event.preventDefault();
                event.stopPropagation();
            })
            var $dg={};
            var initdatagrid = () => {
                $dg = $('#customer_dg').datagrid({
                   ...
    
            }
    
            var reload = () => {
                $dg.datagrid('load', '@Url.Page("/Customers/Index")?handler=Data');
            }
    
            $(() => {
                initdatagrid();
            })
            var popupmodal = (customer) => {
               ...
            }
    
            var onEdit = (index) => {
                var customer = $dg.datagrid('getRows')[index];
                popupmodal(customer);
            }
            var onDelete = (id) => {
                ...
            }
            var onDeleteChecked = () => {
                ...
            }
            var onExport = () => {
               ...
             }
    
    
        </script>
    }
    
    

    我的项目成果

    网站账号/密码截图
    http://tms.i247365.net/ TMS 运输管理系统 demo@email.com/123456 image.png
    http://manage.i247365.net/ 委托业务内控管理系统 demo/123456 image.png
    http://check.i247365.net/ 人脸考勤管理系统 demo/123456 image.png
    http://supplier.i247365.net/ 供应商询价系统 demo/123456 image.png
    http://iot.i247365.net/ 智能水务综合管理平台 demo/123456 image.png
    http://book.i247365.net/ 小型图书管理工具 demo/123456 image.png

    最后

    keep coding, enjoy coding.
    如果你喜欢这个项目,请记得在Github上点赞,谢谢
    https://github.com/neozhu/RazorPageCleanArchitecture

    作者:Leo_wl
             
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    版权信息
  • 相关阅读:
    Android开发 ViewConfiguration View的配置信息类
    Android 开发 倒计时功能 转载
    Android 开发 关于7.0 FileUriExposedException异常 详解
    Android 开发 实现文本搜索功能
    Android 开发 Activity里获取View的宽度和高度 转载
    Android 开发 存储目录的详解
    Android 开发 Fresco框架点击小图显示全屏大图实现 ZoomableDraweeView
    Android 开发 将window变暗
    Android 开发 DisplayMetrics获取Android设备的屏幕高宽与其他信息
    Android 开发 DP、PX、SP转换详解
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/15457261.html
Copyright © 2011-2022 走看看