08
6
原文:bit.ly/3wFqDy9
作者:Daniel
译者:王亮

.NET 6 预览版 4 现已发布,其中包括对 ASP.NET Core 的许多新改进。

下面是此次预览版中 ASP.NET Core 的更新内容:

1 开始使用

要开始使用 .NET 6 Preview 4 中的 ASP.NET Core,请安装 .NET 6 SDK[1]。

如果你在 Windows 上使用 Visual Studio,我们建议安装 Visual Studio 2019 16.11 的最新预览版。如果你在 macOS 上,我们建议安装 Visual Studio 2019 for Mac 8.10 的最新预览版。

2 升级一个现有的项目

要将一个现有的 ASP.NET Core 应用程序从 .NET 6 Preview 3 升级到.NET 6 Preview 4。

将所有 Microsoft.AspNetCore. 引用包更新为 6.0.0-preview.4.

更新所有 Microsoft.Extensions. 引用包更新为 6.0.0-preview.4.

请参阅 .NET 6 中 ASP.NET Core 的完整中断变化列表[2]。

3 引入最小 API

在 .NET 6 中,我们为 Web 应用的托管和路由引入了最小 API。这为使用 .NET 构建第一个 Web 应用程序以及想要构建小型微服务和 HTTP API 的开发者打开了大门。这些精简的 API 提供了 ASP.NET MVC 的优点。

要尝试创建一个最小 API,请创建一个新的 ASP.NET Core 空 Web 应用。

dotnet new web -o MinApi

只需一个文件和几行代码,你现在就有一个功能齐全的 HTTP API。

file

4 新的路由 API

新的路由 API 允许用户路由到任何类型的方法。这些方法可以使用类似控制器的参数绑定、JSON 格式化和 Action 结果。

之前(使用现有的 Map APIs):

app.MapGet("/", async httpContext =>
{
    await httpContext.Response.WriteAsync("Hello World!");
});

现在(使用新的 Map 重载):

app.MapGet("/", (Func<string>)(() => "Hello World!"));

5 C# 10 的改进

这些 API 已经利用了较新的 C# 特性,如顶层语句。在今年晚些时候与 .NET 6 一起发布的 C# 10 中,体验将变得更好。例如,不再需要明确地把类型转换成 (Func)。下面的图片展示了 C# 10 支持的特性:

file

开发者从使用类和方法到使用 lambda,拥有和使用 MVC 控制器及属性操作一样的功能。

6 新的托管(hosting) API

新的空 Web 模板使用的是 .NET 6 Preview 4 中引入的新的托管模式。

var app = WebApplication.Create(args);

app.MapGet("/", (Func<string>)(() => "Hello World!"));

app.Run();

你并不局限于只使用新的路由 API。下面是一个 Web 应用程序的例子,它被更新为使用新的托管模式,配置服务和添加中间件。

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1"));
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

新的托管 API 减少了配置和启动 ASP.NET 应用程序所需的模板数量。

7 性能

这些新的路由 API 的开销比基于控制器的 API 少得多。使用新的路由 API,ASP.NET Core 能够在 TechEmpower[3] JSON 基准测试中达到约 80 万 RPS,而 MVC 则达到约 50 万 RPS。

file

异步流

ASP.NET Core 现在支持从控制器 Action 一直到响应的 JSON 格式化器的异步流。从 Action 中返回 IAsyncEnumerable,在发送之前不再在内存中缓冲响应内容。这有助于在返回可异步枚举的大型数据集时减少内存使用。

请注意,Entity Framework Core 提供了用于查询数据库的 IAsyncEnumerable 的实现。在 .NET 6 中,ASP.NET Core 对 IAsyncEnumerable 的支持有所改进,可以使 EF Core 与 ASP.NET Core 的使用更加高效。例如,下面的代码在发送响应前将不再把 Products 数据缓冲到内存中:

public IActionResult GetProducts()
{
    return Ok(dbContext.Products);
}

然而,如果你已经将 EF Core 设置为使用懒加载,这种新的行为可能会导致在数据被枚举时由于并发的查询执行而产生错误。你可以通过自己缓冲数据来恢复到以前的行为:

public async Task<IActionResult> Products()
{
    return Ok(await dbContext.Products.ToListAsync());
}

有关这一行为变化的更多细节,见相关公告[4]。

8 HTTP 日志中间件

HttpLogging 是一个新的内置中间件,可以记录 HTTP 请求和 HTTP 响应的信息,包括头信息和整个 Body。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseHttpLogging();
}

HttpLogging 中间件提供了以下日志:

HTTP 请求信息

普通属性

头信息

请求 Body

HTTP 响应信息

为了配置 HTTP 日志中间件,你可以在对 ConfigureServices() 的调用中指定 HttpLoggingOptions:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpLogging(logging =>
    {
        // Customize HTTP logging here.
        logging.LoggingFields = HttpLoggingFields.All;
        logging.RequestHeaders.Add("My-Request-Header");
        logging.ResponseHeaders.Add("My-Response-Header");
        logging.MediaTypeOptions.AddText("application/javascript");
        logging.RequestBodyLogLimit = 4096;
        logging.ResponseBodyLogLimit = 4096;
    });
}

这会在日志中产生新的带有 Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware 类别的 HTTP 请求信息。

关于如何使用 HTTP 日志的更多信息,请看 HTTP 日志文档[5]。

9 使用 Kestrel 作为默认启动

file

对于在 .NET 6 Preview 4 中创建的所有新项目,我们已将默认的启动配置文件从 IIS Express 改为 Kestrel。在开发应用程序时,启动 Kestrel 的速度明显加快,并带来了更灵敏的体验。

IIS Express (ms) Kestrel (ms) % change
Debugging 4359 2772 36%
No debugging 1999 727 64%

IIS Express 仍然可以作为启动项,用于 Windows 认证或端口共享等情况。

10 IConnectionSocketFeature

IConnectionSocketFeature 功能使你能够访问与当前请求相关的底层接受 socket。它可以通过 HttpContext 上的 FeatureCollection 访问。

例如,下面的应用程序在接受 socket 上设置 LingerState 属性:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
    {
        var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
        socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
        return next();
    }));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();

11 改进单页应用(SPA)模板

略...(译注:文字太长,懒得翻译了,主要 VS 中的 SPA 模板我从来不用)

12.NET 热重载

最新的 Visual Studio 预览版对.NET Hot 热重载有一些初步的支持。你可能已经注意到在调试你的应用程序时,新的 Apply Code Changes 按钮和调试选项。

Apply Code Changes 按钮将用你所作的代码修改来更新正在运行的应用程序,甚至不需要保存。下面是一个更新 Counter 组件的例子,它的增量从 1 改为 2。请注意,一旦修改被应用,当前的计数不会丢失:

file

Visual Studio 中的 .NET 热重载支持仍在进行中,因此在 ASP.NET Core 应用程序中使用它时有一些限制:

你必须在连接调试器的情况下运行以应用更改。

代码修改只能应用于 C# 文件--还不支持对 Razor 文件(.razor, .cshtml)的修改。

已应用的更改还不能强制更新用户界面,因此需要手动触发用户界面更新。

目前不支持 Blazor WebAssembly 应用程序。

所有这些限制都在解决中,并将在未来的 Visual Studio 更新中得到解决。敬请关注!

如果你通过 dotnet watch 使用 .NET 热重载,修改将被应用于 ASP.NET Core 托管的 Blazor WebAssembly 应用程序。如果你刷新浏览器,修改也会重新应用到你的 Blazor WebAssembly 应用程序。

要了解更多关于.NET 热重载的信息,你可以在我们的博文中获得所有细节:介绍.NET 热重载[6]。

13 Razor 中的泛型约束

在 Razor 中使用 @typeparam 指令定义通用类型参数时,你现在可以使用标准的 C# 语法指定泛型约束。

@typeparam TEntity where TEntity : IEntity

14 Blazor 错误边界

Blazor 错误边界提供了一种方便的方式来处理组件层次结构中的异常情况。为了定义一个错误边界,使用新的 ErrorBoundary 组件来包裹一些现有的内容。只要一切运行顺利,ErrorBoundary 组件将渲染其子内容。如果一个未处理的异常被抛出,ErrorBoundary 会渲染一些错误 UI。

例如,我们可以像这样在默认 Blazor 应用程序的布局中添加一个错误边界:

<div class="main">
  <div class="top-row px-4">
    <a href="https://docs.microsoft.com/aspnet/" target="_blank" rel="noopener">
      About
    </a>
  </div>

  <div class="content px-4">
    <ErrorBoundary> @Body </ErrorBoundary>
  </div>
</div>

该应用程序继续像以前一样运作,但现在我们的错误边界将处理未处理的异常。例如,我们可以更新 Counter 组件,在计数过大时抛出一个异常。


private void IncrementCount()
{
    currentCount++;
    if (currentCount > 10)
    {
        throw new InvalidOperationException("Current count is too big!");
    }
}

现在,如果我们过多地点击计数器,就会抛出一个未处理的异常,这将由我们的错误边界通过渲染一些默认的错误界面来处理。

file

默认情况下,ErrorBoundary 组件为其错误内容渲染了一个带有 blazor-error-boundary CSS 类的空 div。这个默认界面的颜色、文本和图标都是在应用程序中使用 CSS 定义的,所以你可以自由地定制它们。你也可以通过设置 ErrorContent 属性来改变默认的错误内容。

<ErrorBoundary>
  <ChildContent> @Body </ChildContent>
  <ErrorContent>
    <p class="my-error">Nothing to see here right now. Sorry!</p>
  </ErrorContent>
</ErrorBoundary>

因为我们在布局中定义了错误边界,一旦抛出一个异常,不管我们导航到哪个页面我们都能看到错误内容。一般来说,错误边界的范围最好比这更窄,但我们可以选择在随后的页面导航中通过调用错误边界的恢复方法将错误边界重置为非错误状态。

<ErrorBoundary @ref="errorBoundary">
    @Body
</ErrorBoundary>
...

@code {
    ErrorBoundary errorBoundary;

    protected override void OnParametersSet()
    {
        // On each page navigation, reset any error state
        errorBoundary?.Recover();
    }
}

15 Blazor WebAssembly 的 AOT 编译

Blazor WebAssembly 现在支持 AOT(ahead-of-time) 编译,你可以将你的 .NET 代码直接编译为 WebAssembly,以显著提高运行时性能。现在 Blazor WebAssemby 应用程序使用 WebAssembly 中实现的 .NET IL 解释器运行。由于 .NET 代码是被解释的,通常这意味着在 WebAssembly 上运行的 .NET 代码比在正常的.NET 运行时要慢得多。.NET WebAssembly AOT 编译通过将你的 .NET 代码直接编译成 WebAssembly 来解决这个性能问题。

AOT 编译你的 Blazor WebAssembly 应用程序,对于 CPU 密集型任务来说,其性能的提高是相当显著的。例如,下面的片段显示了使用相同的 Blazor WebAssembly 应用程序进行一些基本图像编辑的比较,首先使用解释器,然后是 AOT 编译。AOT 编译后的版本运行速度快了五倍以上。

file

你可以在 GitHub 上查看这个 PictureFixer[7] 的代码。

.NET WebAssembly AOT 编译需要一个额外的构建工具,必须作为一个可选的 .NET SDK 工作负载来安装才能使用。要安装 .NET WebAssembly 构建工具,请运行以下命令:

dotnet workload install microsoft-net-sdk-blazorwebassembly-aot

为了在你的 Blazor WebAssembly 项目中启用 WebAssembly AOT 编译,在你的项目文件中添加以下属性:

true

然后 AOT 将你的应用程序编译成 WebAssembly,发布应用程序。使用 Release 配置发布将确保 .NET IL 链接也被运行,以减少发布应用程序的大小。

dotnet publish -c Release

WebAssembly AOT 编译只在项目发布时进行。当项目在开发过程中运行时,它并不使用。这是因为 WebAssembly AOT 编译可能需要一段时间。

AOT 编译的 Blazor WebAssembly 应用程序的大小通常比作为 .NET IL 的应用程序要大。在我们的测试中,大多数 AOT 编译的 Blazor WebAssembly 应用程序大约大2倍,不过这取决于具体的应用程序。这意味着,使用 WebAssembly 的 AOT 编译,可以用加载时间的性能换取运行时间的性能。这种权衡是否值得,取决于你的应用程序。

16 .NET MAUI Blazor 应用

Blazor 能够用 .NET 构建客户端 Web UI,但有时你需要的东西比 Web 平台提供的更多。有时你需要完全访问设备的本地功能。现在,你可以在 .NET MAUI 应用程序中托管 Blazor 组件,以使用 Web UI 构建跨平台的本地应用程序。这些组件在.NET 进程中原生运行,并使用本地互操作通道向嵌入式 Web 视图控件渲染 Web UI。这种混合方法为你提供了本地和网络的优点。你的组件可以通过 .NET 平台访问本地功能,并呈现标准的 Web UI。.NET MAUI Blazor 应用程序可以运行在任何 .NET MAUI 可以运行的地方(Windows、Mac、iOS 和 Android),尽管我们对 .NET 6的主要关注是在桌面场景。

要创建一个 .NET MAUI Blazor 应用程序,你首先需要在开发机器上配置 .NET MAUI。最简单的方法是使用 maui-check 工具。要安装 maui-check 工具,请运行:

dotnet tool install -g Redth.Net.Maui.Check
然后运行 maui-check 来获取 .NET MAUI 工具和依赖。有关开始使用 .NET MAUI 的其他信息,请参考 GitHub 上的 wiki 文档。

一旦一切安装完毕,使用新的项目模板创建一个 .NET MAUI Blazor 应用程序:

dotnet new maui-blazor -o MauiBlazorApp
你也可以使用 Visual Studio 创建一个 .NET MAUI Blazor 应用程序:

file

.NET MAUI Blazor 应用程序是 .NET MAUI 应用程序,它使用 BlazorWebView 控件将 Blazor 组件渲染到一个嵌入式 Web 视图中。应用程序的代码和逻辑位于 MauiApp 项目中,该项目被设置为多目标 Android、iOS 和 Mac Catalyst。MauiApp.WinUI3 项目用于为 Windows 构建,而 MauiApp.WinUI3(Package) 项目则用于为 Windows 生成 MSIX 包。最终,我们希望将对 Windows 的支持合并到主应用程序项目中,但现在这些独立的项目是必要的。

在 MauiApp 项目的 MainPage.xaml 中设置了 BlazorWebView 控件:

<b:BlazorWebView HostPage="wwwroot/index.html">
    <b:BlazorWebView.RootComponents>
        <b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </b:BlazorWebView.RootComponents>
</b:BlazorWebView>

该应用程序的根 Blazor 组件在 Main.razor 中。其余的 Blazor 组件都在 Pages 和 Shared 目录下。请注意,这些组件与默认 Blazor 模板中使用的组件相同。你可以在你的应用程序中使用现有的 Blazor 组件,而不用改变代码,或者引用包含这些组件的现有类库或包。应用程序的静态资源在 wwwroot 文件夹中。

17 Windows

要在 Windows 下运行该应用程序,你需要使用 Visual Studio 构建和运行。

选择 MauiBlazorApp.WinUI3(Package) 项目作为你的启动项目:

file

同时为目标平台选择 x64:

file

然后你可以按 F5 或 Ctrl+F5,使 WinUI 应用作为本地 Windows 桌面应用运行。

file

18 Android

要在 Android 上运行该应用程序,首先使用 Android SDK 或 Android 设备管理器启动 Android 模拟器。

然后使用以下命令从 CLI 运行该应用程序:

dotnet build MauiBlazorApp -t:Run -f net6.0-android

要从 Visual Studio 在 Android 上运行,选择 MauiBlazorApp 项目作为启动项目:

file

然后在运行按钮下拉菜单中选择 net6.0-android 作为目标框架:

file

然后你可以点击 F5 或 Ctrl+F5,使用安卓模拟器运行该应用程序:

file

19 iOS 和 Mac Catalyst

译注:Mac Catalyst 是一个帮助开发者将 iOS 应用移植到 macOS 上的服务。

要运行 iOS 或 Mac Catalyst 的应用程序,你需要使用一个运行 Big Sur 的 macOS 开发环境。你目前不能从 Windows 开发环境中运行 iOS 或 Mac Catalyst 的应用程序,尽管我们确实期望 .NET MAUI 将支持使用连接的 Mac 构建代理或使用热重启在连接的设备上运行 iOS 应用程序。

要运行 iOS 和 Mac Catalyst 的应用程序,请使用以下命令:

dotnet build MauiBlazorApp -t:Run -f net6.0-ios
dotnet build MauiBlazorApp -t:Run -f net6.0-maccatalyst

file

在这个版本中,.NET MAUI Blazor 应用程序有一些已知的限制:

组件范围内的 CSS 文件(.razor.css)还不能在主 .NET MAUI 项目中使用。这将在未来的更新中得到修复。

了解更多关于 .NET 6 Preview 4 中 .NET MAUI 的新内容[8]。

20 其他性能改进

略...(注:这部分列举了一些社区贡献的提高性能的 PR,没啥可翻译的,感兴趣的同学可以直接在英文原文点击链接查看。)

21 提供反馈

我们希望你喜欢此次 .NET 6 中的 ASP.NET Core 预览版。我们很想听听你对这个版本的体验。在 GitHub[9] 上提交 Issue,让我们知道你的想法。

感谢你尝试使用 ASP.NET Core!

文中链接:

[1]. https://dotnet.microsoft.com/download/dotnet/6.0
[2]. https://docs.microsoft.com/dotnet/core/compatibility/6.0#aspnet-core
[3]. https://www.techempower.com/benchmarks/
[4]. https://github.com/aspnet/Announcements/issues/463
[5]. https://docs.microsoft.com/aspnet/core/fundamentals/http-logging
[6]. https://aka.ms/build2021-hotreload
[7]. https://aka.ms/picture-fixer
[8]. https://devblogs.microsoft.com/dotnet/announcing-net-maui-preview-4
[9]. https://github.com/dotnet/aspnetcore/issues
26
5

社区正式发布.NET 6 Preview4

0
归档:2021年5月分类:C#和.NET

时间过得真快,.NET 5我还没有来得及研究,眼看.NET 6的正式版本马上就要来了,我一直期待这个版本,因为这是LTS版本,我们团队会迁移到这个版本上来。

社区一直在不断推进.NET 6的可用性,Build 2021大会发布了Preview4,这个版本更新很大,可能与最终的版本相差不大,,正式发布RC版本是在8月份,正式发布RTM版本是在11月 ,到时后.NET正式完成.NET Framwork和.NET Core以及Mono的功能统一,还是非常值得期待的。

新产品功能:

System.Text.Json support for IAsyncEnumerable
System.Text.Json: Writable DOM Feature
Microsoft.Extensions.Logging compile-time source generator
System.Linq enhancements
Significantly improved FileStream performance on Windows
Enhanced Date, Time and Time Zone support
CodeGen
.NET Diagnostics: EventPipe for Mono and Improved EventPipe Performance
IL trimming
Single-file publishing
CLI install of .NET 6 SDK Optional Workloads
Built-in SDK version checking
CLI Templates (dotnet new)

另外,还有.NET MAUI或多平台应用UI :允许开发人员构建适用于Windows、MacOS、ios 和android的应用程序,共享单一代码库和模板。
ASP.NET Blazor混合应用程序:帮助开发人员构建基于Web的跨平台桌面体验,从而利用本机设备功能。
对ARM的加强支持:包括 Silicon 和ARM64支持。

05
1

2020年中国.NET开发

在2019年上海中国.NET开发者大会的基础上,2020年12月19日 继续以“开源、共享、创新” 为主题的第二届中国 .NET 开发者峰会(.NET Conf China 2020)在苏州人工智能智能产业创新中心拉开帷幕。微软云(Microsoft Azure)和葡萄城作为本次峰会白金赞助,明源云作为银牌赞助,朝夕教育作为铜牌赞助,泰链智能和递易智能作为星牌赞助,还有很多合作伙伴给予了峰会大力支持!本次大会以线下城市苏州为中心,覆盖北京、上海、深圳、广州、长沙、成都、徐州、山东、西安、武汉等地区,是中国 .NET 开发者的大聚会,今年由于疫情的特殊原因,组委会特意控制大会的参与人数为300人,加强线上直播方便全国的开发者参与,在CSDN和思否的战略媒体合作和线上大会直播支持下,线下参会人数突破了400人(含讲师,志愿者及合作伙伴),CSDN和思否线上直播累计触达突破40万人次。

除了大会外,我们在会前(12月18日)安排了 .NET Conf Eve(带着大家提前了解大会细节),在大会当天(12月19日)安排了采访环节同步直播,通过采访大会讲师,合作伙伴,志愿者,观众等从不同维度报道大会盛况。第二天(12月20日)特别安排了 Blazor 及 NCF 工作坊。让对Coding感兴趣的伙伴们带上电脑,沉浸式现场开发。

本次峰会完全由来自.NET技术圈开发者们的志愿者进行组织。从大家看到的精美海报,到活动现场的许多志愿者、主持人,这些都是来自对.NET 技术充满热情的优秀开发者们;尤其是那些为大家带来精彩内容的老师们,他们有时候为了PPT上的每一页内容,需要花费大量的业余时间;除了这些之外,我们还能看到许多细节,如那些设计精美的PPT、那些设计精良的文化衫、甚至还有那些小巧玲珑的贴纸图案、海报,无一不是来源于社区志愿者的精心奉献。

群英集聚,力求卓越

来自全国各地的超过400名 .NET 开发者受邀参与这次大会,超过40万人次通过直播参与大会,来自各领域的40多位资深讲师就 .NET Core ,跨平台,微服务、K8S、机器学习、大数据、IoT、混沌工程、DevOps以及自主创新等当前最为热门的科技专题做了多场精彩绝伦的演讲,与全国的 .NET 开发者一起共享技术盛宴。如此规模的中国 .NET 开发者峰会能够成功举办离不开社区伙伴、企业伙伴和志愿者的多方努力,他们共同携手,打造了中国最具影响力的 .NET 开发者峰会。

“开源、共享、创新”

这次大会的主题是“开源 共享 创新”, 这象征着中国 .NET 社区走向开放、走向世界、走向辉煌的未来趋势和历史使命。曾经, .NET 技术的发展只能依靠微软,而今,要依靠的不仅仅是微软,更是真正的社区力量。6年来,在微软 CEO 萨提亚·纳德拉的带领下,微软发生了巨大的变化,微软通过成立 .NET 基金会(dotnetfoundation.org)全面扶持开源项目。据 GitHub 最新发布的年度报告显示,按贡献者列出的增长最快的开源项目,用于跨语言和平台构建应用程序和网站的工具包和框架在今年有了显著的增长,全球.NET开发者500万,其中200万已经使用.NET Core, 在中国也有50万.NET开发者,.NET Core开发者也有20万。与此同时, .NET 社区在中国也发生了巨大变革,互联网大时代的一波波浪潮或许不会再滚滚而前,但产业互联网的大势已经拉开,而这意味着未来的时代将会成为 .NET 开发者们的主场,在 github/gitee 上活跃着很多中国的.NET 开源开发者组织,例如NCC、盛派开发者、Abp,龙芯.NET等。龙芯团队正式在大会上发布了龙芯.NET。

本次峰会持续两天,议题涵盖:云原生应用开发、 .NET 在云服务、游戏开发、区块链、大数据、机器学习、物联网的应用等。图文直播曝光量22697人次,主题演讲视频直播高峰在线观看10万人次。

大会亮点 大神云集

.NET 社区汇集全球富有实践经验的讲师、开源贡献者,来自微软、葡萄城、龙芯、明源云、蔚来汽车等超40位.NET领域权威技术专家齐聚苏州。大会开始前,来自苏州工业园区的领导** 进行了大会致辞,微软全渠道事业部CTO徐明强博士为我们带来《.NET,应您所想的应用开发平台》从.NET生态动力、.NET在全球影响力、中国.NET社区和.NET5的技术提升等多个方面微软近年来在.NET5和开源生态上做出的努力,微软大中华区Azure事业部总经理林家伟还带来了 远在美国的Julia Liuson 和 的大咖视频,龙芯.NET项目和JVM项目负责人敖琪在大会上正式发布了龙芯.NET, 来自日本东京的桂素伟线上直播分享了他基于K8S 复杂项目交付等。

2020中国 .NET 开发者峰会携手国内外社区,是当今最具发展前景的盛会,也是国内 .NET 社区近一年来社区发展的集中展示。从南方大湾区、中部长沙武汉、西部成都与西安,到北方的胶东开发者部落,均参与了这次峰会,众多开源社区NCC、盛派开发者、Abp,龙芯.NET等都在本次大会上展现了社区的蓬勃发展。这次峰会是当之无愧的国内 .NET 社区发展集结号。

.NET 基金会引领的开源社区齐聚开源顶级项目,以吸引目标用户、开发者和贡献者。苏州三星高级研发工程师仇华 分享的《.NET 深度学习的工业视觉解决方案》为我们带来了原生的、更高效的利用 .NET 部署人工智能工业解决方案,通过ML.NET和TF.NET的搭配让“ .NET 做人工智能”变得更简单。人大数媒科技(北京)有限公司技术总监黄立敬 带来的《国产化系统下的.NET Core实践》以亲身经验向大家传授.NET 在国产化系统下的实践,给大家树立新时代的.NET 发展信心。

还有众多开源项目的作者在大会上为大家带来他们的开源项目实践经验分享。NCC社区、 盛派开发者社区、ABP社区等的多个开源项目作者 在大会上向社区展现他们的开源解决方案。

大家所认为的.NET 弱势领域 大数据 和前端技术领域,在这次大会上正向大家展示了.NET的无穷潜力。汽车之家搜索工程师贾维维的主题《.NET 5与高性能计算》给我们展示了.NET 5为我们构建大数据生态的技术基础以及他自身在这一领域的尝试,特来电技术架构师 刘佳海 的主题《特来电稳定性保障技术体系实践》为我们展示了他在使用.NET 技术打造新能源充电行业领先的稳定性运维保障技术体系。.NET 基金会项目Ant Design Blazor作者杨舜杰和陈超超带来的基于WebAssembly技术的前端框架Blazor 主题内容从入门到开发实践,展示了.NET 在前端领域的新星。12月20日的工作坊-- 基于 Blazor 的 C# 前端开发实战工作坊 让大家体验用 C# 进行前后端 Web 应用开发的新姿势。

我们已经开始进入万物互联时代,.NET 在物联网时代更具优势,来自特来电 云平台技术负责人周国庆 的主题—基于.NET技术栈的互联网技术平台揭秘,支撑了中国最强新能源汽车充电网的大规模应用,来自蔚来汽车的数字化发展软件开发与创新部的系统架构师朱宗海向大家分享了.NET 在制造业数字化转型,向着智能制造业的转型升级过程中,我们.NET开发人员不仅具有广阔的前景,更有丰厚的钱景。苏州三星高级研发工程师仇华也向我们展示了在制造业的人工智能应用的.NET解决方案。

低代码技术在数字化转型新战场上是今年的热点,大会上有多位讲师带来相关内容,例如明源云架构师唐敦峰 分享的基于Entity Framework Core的元数据驱动架构,程析智能的刘腾飞分享的-.NET Core 面向元数据驱动的架构。

.NET Core 的云原生特性(镜像体积小、占用内存低、启动速度快)非常符合云原生技术的潮流,大会主题内容的一个突出特点恰恰是.NET Core在云原生的实践案例分享。

本次大会由社区主办,来自社区并服务于社区(Everyone comes from community, everyone serves community),后续,由多城市社区主办的 .NET 社区活动也在将不同城市开展。或许,就在你的身边,关于“中国 .NET 发展、开源、共享、创新”的探讨正在火热进行中!

原文地址: https://www.cnblogs.com/shanyou/p/14223309.html

20
12

这两三天一直在苏州,参加2020年中国.NET开发者大会,我第一次以讲师的身份参加技术大会,以前都是作为听众,这一次也是代表公司出去做技术交流,为此做了不少准备工作。

我从大学就是用C#和.NET,一直默默关注C#开源社区,2010年玩过Mono,2013年采用Xamarin开发Android应用,从2019年起开始带来团队实践.NET Core项目,公司核心的产品已经迁移到.NET Core,今年以来带领团队完成国产化系统下的.NET Core项目改造。

我的演讲主题是国产化系统下的.NET Core实践经验和心得,这是我们过去一年多以来的实践和心得,主要包括以下几个重点:

1、国产系统现状和要求,包括CPU、操作系统和中间件的要求,如何突破目前中间件对Java的特殊保护,到底是用java重写还是迁移.NET  Core,给出实用的迁移建议;

我的建议是“尽量不要重复造轮子,坚持使用DotNet”,而解决方案是如下:

file

2、国产化各种准备工作,重点技术预研,特别是现有系统的各个类库对.NET  Core的支持情况,最终做出技术评估;

file

3、项目迁移工作,除了按照官方要求迁移之外,还需要考虑系统在各个平台的可移植情况,我们迁移过程中遇到的问题和解决办法;

1)、现有系统的各个类库对.NET Core的支持情况:官方API和类库基本没有问题;重点注意第三方类库,比如Lucence.NET,Zip解析等
2)、客户迁移的要求情况:如果服务器CPU架构必须是申威,要重视;如果是龙芯可以拖一拖,但是其实可以和客户提系统支持的要求。

4、项目测试,模拟各种国产CPU搭建测试环境,并对系统进行测试;

file

5、国产专用服务器上的系统部署实践,部署过程中的问题和解决方案。

file

结语:开源,自由、开放的社区是创新的力量之源,这也是互联网诞生的初心。今天主持人说到会90后超过一半,还是很希望90后、00后和未来者们都知道什么是网站URL地址,都知道怎么使用搜索引擎,不要只是被困在算法和移动应用之中。

27
9

这两个月来带领团队实践安可国产化系统下的DotNet Core,我们积累了一些经验,这里做一下记录。

1、DotNet Core可以在安可下面运行吗?

首先,答案是肯定的。目前除了龙芯架构的服务器不支持DotNet Core(龙芯团队已经在努力,他们预计会尽快支持DotNet Core的下一个版本),其他所有平台架构的都支持,目前我们实际客户的操作系统有:ARM64(包括其扩展Aarch64)、X64,这些服务器包括飞腾、华为鲲鹏等国产服务器。

2、迁移麻烦吗?工作量有多大?

非常靠谱。DotNet兼容性做得非常好,只要是它支持的架构的服务器,都可以很好地运行。
file
唯一需要考虑的是,如果项目用到的第三方库比较多,需要逐个验证迁移工作,大部分流行的第三方库都已经支持DotNet Core。

3、Java重写还是迁移?

除非你们团队已经没有DotNet开发人员了,否则千万不要用Java重写,一方面是工作量大,另一方面是java的项目换到安可环境也是需要做很多适配工作。
而且我可以很负责任地说:DotNet Core的运行效率超级棒!几乎可以用飞快来形容。

4、迁移过程中有哪些经验?

代码经验

我们的代码几乎无缝转移过去,唯一的地方就是全文检索lucene.net,现在这个项目已经有更新,发了十几个bata版本,我认为可以用,所以升级过来了。
另外,安可要求只能用国产达梦数据库,需要做适配,还好这些数据库基本都是开源改过来的,适配不是问题,就是一些日期等细节需要主义。

服务器经验

我们第一个项目是迁移到政务系统,要求非常严格,有专用的服务器。我们先是在通用测试服务器下(Aarch64架构CPU,中标麒麟操作系统和银河麒麟操作系统),运行起来没有任何问题。后来在专用服务器下,架构和CPU都不变,但是专用服务器涉密,所以不能随意使用命令安装库,也不能更新系统库,只需要找到合适的包用专用服务器工具安装就可以,如果是系统库文件,就让厂商安装。

5、最大的问题是什么?

最大的问题是,安可要求服务器必须通过中间件安装系统,这些中间件其实就是web服务器,比如金蝶之类的,这里很崩溃,因为这些国产中间件只支持java,其实就是另外一个tomcat,我也认为他们基于开源改造。
后来我们的解决办法是,前后端分离,通过金蝶发布前端web站点,后端启动5000端口提供DotNet Core服务,这个并不违法安可要求。DotNet Core基于最开放的MIT开源协议,比java开放多了。

24
8

关于IT国产化盛宴的几点感想

0
归档:2020年8月分类:C#和.NET

这三周来牺牲社交时间,我亲自出马、带着团队三名精干主力在“中标麒麟龙芯”下编译.Net Core 3.0、并调试程序,编译通过但Bug太多,以失败而告终,最后放弃这个平台,这不是我们的问题,这是龙芯团队的问题,他们的CPU对彻底开源的.NET支持台还很差(也许明年会好些),不过目前我们支持“中标麒麟兆芯”,这个平台也完全符合安可要求。

从我接触到的情况来看,目前最鸡血的是那些国字号相关的企业,不过如果不涉密,但大部分也只是买来应付检查,因为国产服务器真的是几乎不能用。还有一些比较聪明的单位,直接购买云国产操作系统,但是这些国产云操作系统就是一个幌子,因为它们也是跑在“洋芯片”、寄生在“洋软件”下面而已。

Java的码农请别吐槽C#,两门语言都是“美帝”发明的开发语言哦,要说彻底支持符合国产要求,别忘了Java的SDK还有专利费,而且Java的拥有者Oracle告谷歌的侵权诉讼案还没结束呢,哈哈。

另外,很想对那些摇着民族主义旗号大喊国产的人,有本事从应用软件到开发语言彻底国产,还有哦,wifi现在澳洲公司还在收专利费、互联网也是MIT的发明、Apache基金会的总部现在还设在美国的马里兰州……这只是一场分蛋糕大盛宴,曲终人必散,苦的是那些重复造轮子的码农,还好我们已经脱离苦海。

16
6

你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案。

本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴领域驱动设计思想,指导微服务项目团队进行设计和开发(理论篇详见《当中台遇上 DDD,我们该如何设计微服务?》)。本文包括三部分内容:第一部分讲述领域驱动设计基本知识,包括:分层架构、服务视图、数据视图和领域事件发布和订阅等;第二部分讲述微服务设计方法、过程、模板、代码目录、设计原则等内容;最后部分以一个项目为例讲述基于 DDD 的微服务设计过程。

目标

本文采用 DDD(领域驱动设计)作为微服务设计指导思想,通过事件风暴建立领域模型,合理划分领域逻辑和物理边界,建立领域对象及服务矩阵和服务架构图,定义符合 DDD 分层架构思想的代码结构模型,保证业务模型与代码模型的一致性。通过上述设计思想、方法和过程,指导团队按照 DDD 设计思想完成微服务设计和开发。

通过领域模型和 DDD 的分层思想,屏蔽外部变化对领域逻辑的影响,确保交付的软件产品是边界清晰的微服务,而不是内部边界依然混乱的小单体。在需求和设计变化时,可以轻松的完成微服务的开发、拆分和组合,确保微服务不易受外部变化的影响,并稳定运行。

适用范围

本文适用于按照 DDD 设计方法进行微服务设计和开发的项目及相关人员。

以下情况不适用:

“我们的目标是按期盖出一栋大楼来,不要跟我提什么方法,有这啰嗦的时间,还不如抓紧点时间搬砖,把楼给我快点盖好!”。

“我的工作就是让软件运行起来,能工作一切就 OK!我不需要那么多约束,什么设计方法、扩展性、业务变化、领域模型、响应能力与我无关。别耽误工期啦!先上线再说!”。

“好的软件是自己演进出来的,我们不需要设计!”。

哈哈,开个玩笑啦!其实设计不会花太多时间的!

正文内容

不耽误大家时间了,言归正传。

DDD 分层架构视图
DDD 分层架构包括:展现层、应用层、领域层和基础层。

基于DDD的微服务设计和开发实战

DDD 分层架构各层职能如下:

展现层
展现层负责向用户显示信息和解释用户指令。

应用层
应用层是很薄的一层,主要面向用户用例操作,协调和指挥领域对象来完成业务逻辑。应用层也是与其他系统的应用层进行交互的必要渠道。应用层服务尽量简单,它不包含业务规则或知识,只为下一层的领域对象协调任务,使它们互相协作。应用层还可进行安全认证、权限校验、分布式和持久化事务控制或向外部应用发送基于事件的消息等。

领域层
领域层是软件的核心所在,它实现全部业务逻辑并且通过各种校验手段保证业务正确性。它包含业务所涉及的领域对象(实体、值对象)、领域服务以及它们之间的关系。它负责表达业务概念、业务状态以及业务规则,具体表现形式就是领域模型。

基础层
基础层为各层提供通用的技术能力,包括:为应用层传递消息、提供 API 管理,为领域层提供数据库持久化机制等。它还能通过技术框架来支持各层之间的交互。

服务视图
微服务内的服务视图
微服务内有 Facade 接口、应用服务、领域服务和基础服务,各层服务协同配合,为外部提供服务。

基于DDD的微服务设计和开发实战

1、接口服务
接口服务位于用户接口层,用于处理用户发送的 Restful 请求和解析用户输入的配置文件等,并将信息传递给应用层。

2、应用服务
应用服务位于应用层。用来表述应用和用户行为,负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装。

应用层的服务包括应用服务和领域事件相关服务。

应用服务可对微服务内的领域服务以及微服务外的应用服务进行组合和编排,或者对基础层如文件、缓存等数据直接操作形成应用服务,对外提供粗粒度的服务。

领域事件服务包括两类:领域事件的发布和订阅。通过事件总线和消息队列实现异步数据传输,实现微服务之间的解耦。

3、领域服务
领域服务位于领域层,为完成领域中跨实体或值对象的操作转换而封装的服务,领域服务以与实体和值对象相同的方式参与实施过程。

领域服务对同一个实体的一个或多个方法进行组合和封装,或对多个不同实体的操作进行组合或编排,对外暴露成领域服务。领域服务封装了核心的业务逻辑。实体自身的行为在实体类内部实现,向上封装成领域服务暴露。

为隐藏领域层的业务逻辑实现,所有领域方法和服务等均须通过领域服务对外暴露。

为实现微服务内聚合之间的解耦,原则上禁止跨聚合的领域服务调用和跨聚合的数据相互关联。

4、基础服务
基础服务位于基础层。为各层提供资源服务(如数据库、缓存等),实现各层的解耦,降低外部资源变化对业务逻辑的影响。

基础服务主要为仓储服务,通过依赖反转的方式为各层提供基础资源服务,领域服务和应用服务调用仓储服务接口,利用仓储实现持久化数据对象或直接访问基础资源。

微服务外的服务视图

  1. 前端应用与微服务
    微服务中的应用服务通过用户接口层组装和数据转换后,发布在 API 网关,为前端应用提供数据展示服务。

  2. 微服务与外部应用
    跨微服务数据处理时,对实时性要求高的场景,可选择直接调用应用服务的方式(新增和修改类型操作需关注事务一致性)。对实时性要求不高的场景,可选择异步化的领域事件驱动机制(最终数据一致性)。

    数据视图
    DDD 分层架构中数据对象转换的过程如下图。

基于DDD的微服务设计和开发实战

数据视图应用服务通过数据传输对象(DTO)完成外部数据交换。领域层通过领域对象(DO)作为领域实体和值对象的数据和行为载体。基础层利用持久化对象(PO)完成数据库的交换。

DTO 与 VO 通过 Restful 协议实现 JSON 格式和对象转换。

前端应用与应用层之间 DTO 与 DO 的转换发生在用户接口层。如微服务内应用服务需调用外部微服务的应用服务,则 DTO 的组装和 DTO 与 DO 的转换发生在应用层。

领域层 DO 与 PO 的转换发生在基础层。

领域事件和事件总线
领域事件是领域模型中非常重要的部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,有助于形成完整的业务闭环。领域事件主要用于解耦微服务,各个微服务之间不再是强一致性,而是基于事件的最终一致性。

基于DDD的微服务设计和开发实战

微服务内的领域事件
微服务内的领域事件可以通过事件总线或利用应用服务实现不同聚合之间的业务协同。当微服务内发生领域事件时,由于大部分事件的集成发生在同一个线程内,不一定需要引入消息中间件。但一个事件如果同时更新多个聚合数据,按照 DDD“一个事务只更新一个聚合根”的原则,可以考虑引入消息中间件,通过异步化的方式,对微服务内不同的聚合根采用不同的事务。

微服务之间的领域事件
微服务之间的数据交互方式通常有两种:应用服务调用和领域事件驱动机制。

领域事件驱动机制更多的用于不同微服务之间的集成,实现微服务之间的解耦。事件库(表)可以用于微服务之间的数据对账,在应用、网络等出现问题后,可以实现源和目的端的数据比对,在数据暂时不一致的情况下仍可根据这些数据完成后续业务处理流程,保证微服务之间数据的最终一致性。

应用服务调用方式通常应用于实时性要求高的业务场景,但一旦涉及到跨微服务的数据修改,将会增加分布式事务控制成本,影响系统性能,微服务之间的耦合度也会变高。

事件总线
事件总线位于基础层,为应用层和领域层服务提供事件消息接收和分发等服务。其大致流程如下:

1、服务触发并发布事件。

2、事件总线事件分发。

如果是微服务内的订阅者(微服务内的其它聚合),则直接分发到指定订阅者。

如果是微服务外的订阅者,则事件消息先保存到事件库(表)并异步发送到消息中间件。

如果同时存在微服务内和外订阅者,则分发到内部订阅者,并将事件消息保存到事件库(表)并异步发送到消息中间件。为了保证事务的一致性,事件表可以共享业务数据库。也可以采用多个微服务共享事件库的方式。当业务操作和事件发布操作跨数据库时,须保证业务操作和事件发布操作数据的强一致性。

事件数据持久化
事件数据的持久化存储可以有两种方案,在项目实施过程中根据具体场景选择最佳方案。

事件数据保存到微服务所在业务数据库的事件表中,利用本地事务保证业务操作和事件发布操作的强一致性。

事件数据保存到多个微服务共享的事件库中。需要注意的一点是:这时业务操作和事件发布操作会跨数据库操作,须保证事务的强一致性(如分布式事务机制)。

事件数据的持久化可以保证数据的完整性,基于这些数据可以完成跨微服务数据的一致性比对。

微服务设计方法
事件风暴
本阶段主要完成领域模型设计。

基于 DDD 的微服务设计通常采用事件风暴方法。通过事件风暴完成领域模型设计,划分出微服务逻辑边界和物理边界,定义领域模型中的领域对象,指导微服务设计和开发。事件风暴通常包括产品愿景、场景分析、领域建模、微服务设计和拆分等过程。本文不对事件风暴详细方法做深入描述,如感兴趣可查阅相关资料。

1、产品愿景
产品愿景是对产品的顶层价值设计,对产品目标用户、核心价值、差异化竞争点等信息达成一致,避免产品偏离方向。建议参与角色:业务需求方、产品经理和开发组长。

2、场景分析
场景分析是从用户视角出发,探索业务领域中的典型场景,产出领域中需要支撑的场景分类、用例操作以及不同子域之间的依赖关系,用以支撑领域建模。

建议参与角色:产品经理、需求分析人员、架构师、开发组长和测试组长。

3、领域建模
领域建模是通过对业务和问题域进行分析,建立领域模型,向上通过限界上下文指导微服务边界设计,向下通过聚合指导实体的对象设计。

建议参与角色:领域专家、产品经理、需求分析人员、架构师、开发组长和测试组长。

4、微服务拆分和设计
结合业务限界上下文与技术因素,对服务的粒度、分层、边界划分、依赖关系和集成关系进行梳理,完成微服务拆分和设计。

微服务设计应综合考虑业务职责单一、敏态与稳态业务分离、非功能性需求(如弹性伸缩要求、安全性等要求)、团队组织和沟通效率、软件包大小以及技术异构等因素。

建议参与角色:产品经理、需求分析人员、架构师、开发组长和测试组长。

领域对象及服务矩阵和代码模型设计
本阶段完成领域对象及服务矩阵文档以及微服务代码模型设计。

1、领域对象及服务矩阵
根据事件风暴过程领域对象和关系,对产出的限界上下文、聚合、实体、值对象、仓储、事件、应用服务、领域服务等领域对象以及各对象之间的依赖关系进行梳理,确定各对象在分层架构中的位置和依赖关系,建立领域对象分层架构视图,为每个领域对象建立与代码模型对象的一一映射。

建议参与角色:架构师和开发组长。

2、微服务代码模型
根据领域对象在 DDD 分层架构中所在的层、领域类型、与代码对象的映射关系,定义领域对象在微服务代码模型中的包、类和方法名称等,设计微服务工程的代码层级和代码结构,明确各层间的调用关系。

建议参与角色:架构师和开发组长。

领域对象及服务矩阵样例说明
领域对象及服务矩阵主要用来记录事件风暴和微服务设计过程中产出的领域对象属性,如:各领域对象在 DDD 分层架构中的位置、属性、依赖关系以及与代码对象的映射关系等。通过建立领域对象与代码对象的映射关系,可指导软件开发人员准确无误的按照设计文档完成微服务开发。

以下为领域对象及服务矩阵样例(部分数据,仅供参考)。

基于DDD的微服务设计和开发实战

各栏说明如下:

层:定义领域对象位于 DDD 分层架构中的哪一层。如:接口层、应用层、领域层以及基础层等。

聚合:在事件风暴过程中将关联紧密的实体和值对象等组合形成聚合。本栏说明聚合名称。

领域对象名称:领域模型中领域对象的具体名称。如:“请假审批已通过”是类型为“事件”的领域对象;“请假单”是领域类型为“实体”的领域对象。

领域类型:在领域模型中根据 DDD 知识域定义的领域对象的类型,如:限界上下文、聚合、聚合根(实体)、实体、值对象、事件、命令、应用服务、领域服务和仓储服务等。

依赖对象名称:根据业务对象依赖或分层调用依赖关系建立的领域对象的依赖关系(如服务调用依赖、关联对象聚合等)。本栏说明领域对象需依赖的其他领域对象,如上层服务在组合和编排过程中对下层服务的调用依赖、实体之间或者实体与值对象在聚合内的依赖等。

包名:代码模型中的包名,本栏说明领域对象所在的软件包。

类名:代码模型中的类名,本栏说明领域对象的类名。

方法名:代码模型中的方法名,本栏说明领域对象实现或操作的方法名。

微服务代码结构模型
微服务代码模型最终结果来源于领域对象及服务矩阵。在代码模型设计时须建立领域对象和代码对象的一一映射,保证业务模型与代码模型的一致性,即使不熟悉业务的开发人员或者不熟悉代码的业务人员也可以很快定位到代码位置。

微服务代码总目录 基于 DDD 的代码模型包括 interfaces、application、domain 和 infrastructure 四个目录。

基于DDD的微服务设计和开发实战

Interfaces(用户接口层):本目录主要存放用户接口层代码。前端应用通过本层向应用服务获取展现所需的数据。本层主要用于处理用户发送的 Restful 请求和解析用户输入的配置文件等,并将信息传递给 Application 层。主要代码形态是数据组装以及 Facade 接口等。

Application(应用层):本目录主要存放应用层代码。应用服务代码基于微服务内的领域服务或微服务外的应用服务完成服务编排和组合。为用户接口层提供各种应用数据展现支持。主要代码形态是应用服务和领域事件等。

Domain(领域层):本目录主要存放领域层代码。本层代码主要实现核心领域逻辑,其主要代码形态是实体类方法和领域服务等。

Infrastructure(基础层):本目录存放基础层代码,为其它各层提供通用技术能力、三方软件包、配置和基础资源服务等。

用户接口层代码模型
用户接口层代码模型目录包括:assembler、dto 和 facade。

基于DDD的微服务设计和开发实战

Assembler:实现 DTO 与领域对象之间的相互转换和数据交换。理论上 Assembler 总是与 DTO 一同被使用。

Dto:数据传输的载体,内部不存在任何业务逻辑,通过 DTO 把内部的领域对象与外界隔离。

Facade:提供较粗粒度的调用接口,将用户请求委派给一个或多个应用服务进行处理。

应用层代码模型
应用层代码模型目录包括:event 和 service。

基于DDD的微服务设计和开发实战

Event(事件):事件目录包括两个子目录:publish 和 subscribe。publish 目录主要存放微服务内领域事件发布相关代码。subscribe 目录主要存放微服务内聚合之间或外部微服务领域事件订阅处理相关代码。为了实现领域事件的统一管理,微服务内所有领域事件(包括应用层和领域层事件)的发布和订阅处理都统一放在应用层。

Service(应用服务):这里的服务是应用服务。应用服务对多个领域服务或外部应用服务进行封装、编排和组合,对外提供粗粒度的服务。

领域层代码模型
微服务领域层包括一个或多个聚合代码包。标准的聚合代码模型包括:entity、repository 和 service 三个子目录。

基于DDD的微服务设计和开发实战

Aggregate(聚合):聚合代码包的根目录,实际项目中以实际业务属性的名称来命名。聚合定义了领域对象之间的关系和边界,实现领域模型的内聚。

Entity(实体):存放实体(含聚合根、实体和值对象)相关代码。同一实体所有相关的代码(含对同一实体类多个对象操作的方法,如对多个对象的 count 等)都放在一个实体类中。

Service(领域服务):存放对多个不同实体对象操作的领域服务代码。这部分代码以领域服务的形式存在,在设计时一个领域服务对应一个类。

Repository(仓储):存放聚合对应的查询或持久化领域对象的代码,通常包括仓储接口和仓储实现方法。为了方便聚合的拆分和组合,我们设定一个原则:一个聚合对应一个仓储。

特别说明:按照 DDD 分层原则,仓储实现本应属于基础层代码,但为了微服务代码拆分和重组的便利性,我们把聚合的仓储实现代码放到了领域层对应的聚合代码包内。如果需求或者设计发生变化导致聚合需要拆分或重新组合时,我们可以聚合代码包为单位,轻松实现微服务聚合的拆分和组合。

基础层代码模型
基础层代码模型包括:config 和 util 两个子目录。

Config:主要存放配置相关代码。

Util:主要存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码,可为不同的资源类别建立不同的子目录。

基于DDD的微服务设计和开发实战

微服务总目录结构
微服务总目录结构如下:

基于DDD的微服务设计和开发实战

微服务设计原则
微服务设计原则中如高内聚低耦合、复用、单一职责等原则在此就不赘述了,这里主要强调以下几条:

第一条:“要领域驱动设计,而不是数据驱动设计,也不是界面驱动设计”。
微服务设计首先应建立领域模型,确定逻辑和物理边界后,然后才进行微服务边界拆分,而不是一上来就定义数据库表结构,也不是界面需要什么,就去调整领域逻辑代码。

领域模型和领域服务应具有高度通用性,通过接口层和应用层屏蔽外部变化对业务逻辑的影响,保证核心业务功能的稳定性。

第二条:“要边界清晰的微服务,而不是泥球小单体”。
微服务完成开发后其功能和代码也不是一成不变的。随着需求或设计变化,微服务内的代码也会分分合合。逻辑边界清晰的微服务,可快速实现微服务代码的拆分和组合。DDD 思想中的逻辑边界和分层设计也是为微服务各种可能的分分合合做准备的。

微服务内聚合与聚合之间的领域服务以及数据原则上禁止相互产生依赖。如有必要可通过上层的应用服务编排或者事件驱动机制实现聚合之间的解耦,以利于聚合之间的组合和拆分。

第三条:“要职能清晰的分层,而不是什么都放的大箩筐”。
分层架构中各层职能定位清晰,且都只能与其下方的层发生依赖,也就是说只能从外层调用内层服务,内层服务通过封装、组合或编排对外逐层暴露,服务粒度由细到粗。

应用层负责服务的编排和组合,领域层负责领域业务逻辑的实现,基础层为各层提供资源服务。

第四条:“要做自己能 hold 住的微服务,而不是过度拆分的微服务”。
微服务的过度拆分必然会带来软件维护成本的上升,如:集成成本、运维成本以及监控和定位问题的成本。企业转型过程中很难短时间内提升这些能力,如果项目团队不具备这些能力,将很难 hold 住这些过细的微服务。而如果我们在微服务设计之初就已经定义好了微服务内的逻辑边界,项目初期我们可以尽可能少的拆分出过细的微服务,随着技术的积累和时间的推移,当我们具有这些能力后,由于微服务内有清晰的逻辑边界,这时就可以随时根据需要轻松的拆分或组合出新的微服务。

不同场景的微服务设计
微服务的设计先从领域建模开始,领域模型是微服务设计的核心,微服务是领域建模的结果。在微服务设计之前,请先判断你的业务是否聚焦在领域和领域逻辑。

实际在做系统设计时我们可能面临各种不同的情形,如从传统单体拆分为多个微服务,也可能是一个全新领域的微服务设计(如创业中的应用),抑或是将一个单体中面临问题或性能瓶颈的模块拆分为微服务而其余功能仍为单体的情况。

下面分几类不同场景说明如何进行微服务和领域模型设计。

新建系统的微服务设计
新建系统会遇到复杂和简单领域两种场景,两者的领域建模过程也会有所差别。

1、简单领域的建模
对于简单的业务领域,一个领域可能就是一个小的子域。领域建模过程相对简单,根据事件风暴可以分解出事件、命令、实体、聚合和限界上下文等,根据领域模型和微服务拆分原则设计出微服务即可。

2、复杂领域的建模
对于复杂的业务领域,领域可能还需要拆分为子域,甚至子域还会进一步拆分,如:保险领域可以拆分为承保、理赔、收付费和再保等子域,承保子域还可以再拆分为投保、保单管理等子子域。对于这种复杂的领域模型,是无法通过一个事件风暴完成领域建模的,即使能完成,其工程量也是非常浩大,效果也不一定好。

对于这种复杂的领域,我们可以分三阶段来完成领域模型和微服务设计。

拆分子域建立领域模型:根据业务特点考虑流程节点或功能模块等边界因素(微服务最终的拆分结果很多时候跟这些边界因素有一定的相关性),按领域逐级分解为大小合适的子域,针对子域进行事件风暴,记录领域对象、聚合和限界上下文,初步确定各级子域的领域模型。

领域模型微调:梳理领域内所有子域的领域模型,对各子域模型进行微调,这个过程重点考虑不同限界上下文内聚合的重新组合,同步需要考虑子域、限界上下文以及聚合之间的边界、服务以及事件之间的依赖关系,确定最终的领域模型。

微服务设计和拆分:根据领域模型的限界上下文和微服务的拆分原则,完成微服务的拆分和设计。

单体遗留系统的微服务设计
如果一个单体遗留系统,只是将面临问题或性能瓶颈的模块拆分为微服务,而其余功能仍为单体。我们只需要将这些特定功能领域理解为一个简单的子领域,按照简单领域建模方式进行领域模型的设计即可。但在新微服务设计中需要考虑新老系统之间的服务协议,必要时引入防腐层。

特别说明
虽然有些业务领域在事件风暴后发现无法建立领域模型,如数据处理或分析类场景,但本文所述的分层架构模型、服务之间规约和代码目录结构在微服务设计和开发中仍然是通用的。

基于 DDD 的微服务设计和开发实例
为了更好的理解 DDD 的设计思想和过程,我们用一个场景简单但基本涵盖 DDD 设计思想的项目来说明微服务设计和开发过程。

项目基本信息
项目主要目标是实现在线请假和考勤管理。基本功能包括:请假、考勤以及人员管理等。

请假:请假人填写请假单提交审批,根据请假人身份和请假天数进行校验,根据审批规则逐级递交审批,核批通过则完成审批。

考勤:根据考勤规则,剔除请假数据后,对员工考勤数据进行校验,输出考勤统计表。

人员管理:维护人员基本信息和上下级关系。

设计和实施步骤
步骤一:事件风暴
由于项目目标基本明确,我们在事件风暴过程中裁剪了产品愿景,直接从用户旅程和场景分析开始。

1、场景分析:场景分析是一个发散的过程。根据不同角色的旅程和场景分析,尽可能全面的梳理从前端操作到后端业务逻辑发生的所有操作、命令、领域事件以及外部依赖关系等信息(如下图),如:请假人员会执行创建请假信息操作命令,审批人员会执行审批操作,请假审批通过后会产生领域事件,通知邮件系统反馈请假人员结果,并将请假数据发送到考勤以便核销等。在记录这些领域对象的同时,我们也会标记各对象在 DDD 中的层和对象类型等属性,如:应用服务、领域服务、事件和命令等类型。

基于DDD的微服务设计和开发实战

2、领域建模:领域建模是一个收敛的过程。这个收敛过程分三步:第一步根据场景分析中的操作集合定义领域实体;第二步根据领域实体业务关联性,定义聚合;第三步根据业务及语义边界等因素,定义限界上下文。

定义领域实体:在场景分析过程中梳理完操作、命令、领域事件以及外部依赖关系等领域对象后。分析这些操作应由什么实体发起或产生,从而定义领域实体对象,并将这些操作与实体进行关联。

在请假场景中,经分析需要有请假单实体对象,请假单实体有创建请假信息以及修改请假信息等操作。

定义聚合:将业务紧密相关的实体进行组合形成聚合,同时确定聚合中的聚合根、值对象和实体。经分析项目最终形成三个聚合:人员管理、请假和考勤。在请假聚合中有请假单、审批轨迹、审批规则等实体,其中请假单是聚合根,审批轨迹是请假单的值对象,审批规则是辅助实体。

聚合内须保证业务操作的事务性,高度内聚的实体对象可自包含完成本领域功能。聚合是可拆分为微服务的最小单元。在同一限界上下文内多个聚合可以组合为一个微服务。如有必要,也可以将某一个聚合独立为微服务。

定义限界上下文:根据领域及语义边界等因素确定限界上下文,将同一个语义环境下的一个或者多个聚合放在一个限界上下文内。由于人员管理与请假聚合两者业务关联紧密,共同完成人员请假功能,两者一起构成请假限界上下文,考勤聚合则单独形成考勤限界上下文。

3、微服务设计和拆分:理论上一个限界上下文可以设计为一个微服务,但还需要综合考虑多种外部因素,如:职责单一性、性能差异、版本发布频率、团队沟通效率和技术异构等要素。

由于本项目微服务设计受技术以及团队等因素影响相对较小,主要考虑职责单一性,因此根据限界上下文直接拆分为请假和考勤两个微服务。其中请假微服务包含人员和请假两个聚合,考勤微服务只包含考勤聚合。

步骤二、领域对象及服务矩阵
将事件风暴中产出的领域对象按照各自所在的微服务进行分类,定义每个领域对象在微服务中的层、领域类型和依赖的领域对象等。

这个步骤最关键的工作是确定实体、方法、服务等领域对象在微服务分层架构中的位置以及各对象之间的依赖关系,形成服务矩阵(如下表)。这个过程也将在事件风暴数据的基础上,进一步细化领域对象以及它们之间关系,并补充事件风暴中可能遗漏的细节。

确定完各领域对象的属性后,按照代码模型设计各个领域对象在代码模型中的代码对象(包括代码对象所在的:包名、类名和方法名),建立领域对象与代码对象的一一映射关系。根据这种映射关系,相关人员可快速定位到业务逻辑所在的代码位置。

基于DDD的微服务设计和开发实战

步骤三:领域模型及服务架构
根据领域模型中领域对象属性以及服务矩阵,画出领域对象及服务架构视图(如下图)。这个视图可以作为标准的 DDD 分层领域服务架构视图模型,应用在不同的领域模型中。这个模型可以清晰的体现微服务内实体、聚合之间的关系,各层服务之间的依赖关系以及应用层服务组合和编排的关系,微服务之间的服务调用以及事件驱动的前后处理逻辑关系。

在这个阶段,前端的设计也可以同步进行,在这里我们用到了微前端的设计理念,为请假和考勤微服务分别设计了请假和考勤微前端,基于微前端和微服务,形成从前端到后端的业务逻辑自包含组件。两个微前端之上有一个集成主页面,可根据页面流动态加载请假和考勤的微前端页面。(微前端技术详见:《中台微服务了,那前端呢?》)。

基于DDD的微服务设计和开发实战

步骤四:代码模型设计
根据 DDD 的代码结构模型和各领域对象在所在的包、类和方法,定义出请假微服务的代码结构模型。应用层代码结构包括:应用服务以及事件发布相关代码(如下图)。

基于DDD的微服务设计和开发实战

领域层代码结构包括一个或多个聚合的实体类以及领域服务相关代码(如下图)。在本项目中请假微服务领域层包含了请假和人员两个聚合。

基于DDD的微服务设计和开发实战

领域模型中的一个聚合对应一个聚合代码包,如:人员和请假领域逻辑代码都放在各自的聚合代码包中,如随着业务发展,人员管理功能需要从请假微服务中拆分出来,我们只需要将人员聚合代码包稍加改造并独立部署即可快速发布为人员管理微服务。

步骤五:详细设计
在完成领域模型和代码模型设计后,我们就可以开始详细设计了,详细设计主要结合具体的业务功能来开展,主要工作包括:系统界面、数据库表以及字段、服务参数规约及功能等。

步骤六:代码开发
软件开发人员只需要按照设计文档和功能要求,找到业务功能对应的代码位置,完成代码开发和服务编排即可。

步骤七:测试和发布
完成代码开发后,由开发人员编写单元测试用例,基于挡板模拟依赖对象完成跨服务的测试。单元测试完成后,在团队内可进一步完成微服务与相应微前端的集成和测试,形成请假和考勤两个业务组件。前端主页面完成请假和考勤微前端页面集成和页面流及组件基础数据配置,主页面可以按照页面流程动态加载请假和考勤微前端页面。最终部署的软件包包括:请假和考勤两个微服务,请假和考勤两个微前端,一个主页面共计五个。这五个部署包独立开发、独立运行和独立部署。

技术组件说明
主页面和微前端采用:Vue(前端框架),ElementUI(UI 框架 -PC),VUX(UI 框架 - 移动端) 和 MPVUE(UI 框架 - 小程序) 等。微服务开发采用:Spring Cloud、Kafka、Redis 等。数据库采用:PostgreSQL。

附录一:DDD 名词和术语
Event Storming(事件风暴):事件风暴是一项团队活动,旨在通过领域事件识别出聚合根,进而划分微服务的限界上下文。在活动中,团队先通过头脑风暴的形式罗列出领域中所有的领域事件,整合之后形成最终的领域事件集合,然后对于每一个事件,标注出导致该事件的命令(Command),再然后为每个事件标注出命令发起方的角色,命令可以是用户发起,也可以是第三方系统调用或者是定时器触发等。最后对事件进行分类整理出聚合根以及限界上下文。

Entity(实体):每个实体是唯一的,并且可以相当长的一段时间内持续地变化。我们可以对实体做多次修改,故一个实体对象可能和它先前的状态大不相同。但是,由于它们拥有相同的身份标识,他们依然是同一个实体。例如一件商品在电商商品上下文中是一个实体,通过商品中台唯一的商品 id 来标示这个实体。

ValueObject(值对象):值对象用于度量和描述事物,当你只关心某个对象的属性时,该对象便可作为一个值对象。实体与值对象的区别在于唯一的身份标识和可变性。当一个对象用于描述一个事物,但是又没有唯一标示,那么它就是一个值对象。例如商品中的商品类别,类别就没有一个唯一标识,通过图书、服装等这些值就能明确表示这个商品类别。

Aggregate(聚合):聚合是实体的升级,是由一组与生俱来就密切相关实体和值对象组合而成的,整个组合的最上层实体就是聚合。

Bounded Context(限界上下文):用来封装通用语言和领域对象,为领域提供上下文语境,保证在领域之内的一些术语、业务相关对象等(通用语言)有一个确切的含义,没有二义性。使团队所有成员能够明确地知道什么必须保持一致,什么必须独立开发。

04
6

今天邮箱收到github的issue更新提醒,就在昨天,龙芯(loongson)的码农们已经让.NET Core 3.1的FlightFinder例子在MIPS64跑起来了,但是更复杂的程序还得继续改,预计在今年年底加入.NET 5.x(谨慎乐观)。

.NET 本身是完全开源,比Java还要开放(想想现在Google和Oracle关于Java的专利案还没结束呢),但是MS官方已经明确.NET Core和未来的版本不会支持MIPS的CPU指令集架构(龙芯就是用这个架构级),因为太小众了,要实现就得社区自己去写。

龙芯的团队从去年11月份跑Hello World到现在做了不少工作,看社区里的几位工程师也很努力,最终应该会支持(大概率版本会落后),这个值得肯定。但是,对于龙芯来说,社区和生态太难了,要不是去年“安可”要求,估计也不会刺激他们去支持.NET Core。好的结局是“安可”推广成功,gov和army里无关紧要的系统都用(核心系统因为复杂度,怕是很难用上)。

最最要命的是:技术发展太快了,龙芯所支持的服务器市场现在已经完全靠云计算了,这些云计算不可能采用龙芯等国产芯片,这也是为什么阿里要造出概念化的飞天国产云系统的原因——为了政策需要啊!

对于科学技术,我坚信市场化和资本化是最有效的驱动力,最近的SpaceX是又一个有力佐证。努力的人很多,最后大都做了前浪。

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。