当前位置: 首页 > 软件库 > Web应用开发 > Web框架 >

Architecture

授权协议 MIT License
开发语言 JavaScript
所属分类 Web应用开发、 Web框架
软件类型 开源软件
地区 不详
投 递 者 柴衡
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

Architecture

https://rafaelfgx-architecture.herokuapp.com

This project is an example of architecture using new technologies and best practices.

The goal is to share knowledge and use it as reference for new projects.

Thanks for enjoying!

Technologies

Practices

  • Clean Architecture
  • Clean Code
  • SOLID Principles
  • Separation of Concerns
  • DDD (Domain-Driven Design)

Run

Command Line

Prerequisites

Steps

  1. Open directory source\Web\Frontend in command line and execute npm i.
  2. Open directory source\Web in command line and execute dotnet run.
  3. Open https://localhost:8090.
Visual Studio Code

Prerequisites

Steps

  1. Open directory source\Web\Frontend in command line and execute npm i.
  2. Open source directory in Visual Studio Code.
  3. Press F5.
Visual Studio

Prerequisites

Steps

  1. Open directory source\Web\Frontend in command line and execute npm i.
  2. Open source\Architecture.sln in Visual Studio.
  3. Set Architecture.Web as startup project.
  4. Press F5.
Docker

Prerequisites

Steps

  1. Execute docker-compose up --build -d in root directory.
  2. Open http://localhost:8090.

Nuget Packages

Source: https://github.com/rafaelfgx/DotNetCore

Published: https://www.nuget.org/profiles/rafaelfgx

Layers

Web: Frontend and API.

Application: Flow control.

Domain: Business rules and domain logic.

Model: Data transfer objects.

Database: Data persistence.

Web

Frontend

Service

export class AppCustomerService {
    constructor(private readonly http: HttpClient, private readonly gridService: GridService) { }

    add = (customer: Customer) => this.http.post<number>("customers", customer);

    delete = (id: number) => this.http.delete(`customers/${id}`);

    get = (id: number) => this.http.get<Customer>(`customers/${id}`);

    grid = (parameters: GridParameters) => this.gridService.get<Customer>("customers/grid", parameters);

    inactivate = (id: number) => this.http.patch(`customers/${id}/inactivate`, {});

    list = () => this.http.get<Customer[]>("customers");

    update = (customer: Customer) => this.http.put(`customers/${customer.id}`, customer);
}

Guard

export class AppGuard implements CanActivate {
    constructor(private readonly appAuthService: AppAuthService) { }

    canActivate() {
        if (this.appAuthService.authenticated()) { return true; }
        this.appAuthService.signin();
        return false;
    }
}

ErrorHandler

export class AppErrorHandler implements ErrorHandler {
    constructor(private readonly appModalService: AppModalService) { }

    handleError(error: any) {
        if (error instanceof HttpErrorResponse) {
            switch (error.status) {
                case 422: { this.appModalService.alert(error.error); return; }
            }
        }

        console.error(error);
    }
}

HttpInterceptor

export class AppHttpInterceptor implements HttpInterceptor {
    constructor(private readonly appAuthService: AppAuthService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler) {
        request = request.clone({
            setHeaders: { Authorization: `Bearer ${this.appAuthService.token()}` }
        });

        return next.handle(request);
    }
}

API

Startup

public sealed class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application.UseException();
        application.UseHttps();
        application.UseRouting();
        application.UseResponseCompression();
        application.UseAuthentication();
        application.UseAuthorization();
        application.UseEndpointsMapControllers();
        application.UseOpenApiSpecification();
        application.UseSpa();
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSecurity();
        services.AddResponseCompression();
        services.AddControllersWithJsonOptionsAndAuthorizationPolicy();
        services.AddSpa();
        services.AddContext();
        services.AddServices();
        services.AddOpenApiSpecification();
    }
}

Controller

[ApiController]
[Route("customers")]
public sealed class CustomerController : ControllerBase
{
    private readonly ICustomerService _customerService;

    public CustomerController(ICustomerService customerService) => _customerService = customerService;

    [HttpPost]
    public IActionResult Add(CustomerModel model) => _customerService.AddAsync(model).ApiResult();

    [HttpDelete("{id}")]
    public IActionResult Delete(long id) => _customerService.DeleteAsync(id).ApiResult();

    [HttpGet("{id}")]
    public IActionResult Get(long id) => _customerService.GetAsync(id).ApiResult();

    [HttpGet("grid")]
    public IActionResult Grid([FromQuery] GridParameters parameters) => _customerService.GridAsync(parameters).ApiResult();

    [HttpPatch("{id}/inactivate")]
    public IActionResult Inactivate(long id) => _customerService.InactivateAsync(id).ApiResult();

    [HttpGet]
    public IActionResult List() => _customerService.ListAsync().ApiResult();

    [HttpPut("{id}")]
    public IActionResult Update(CustomerModel model) => _customerService.UpdateAsync(model).ApiResult();
}

Application

Service

public sealed class CustomerService : ICustomerService
{
    private readonly ICustomerFactory _customerFactory;
    private readonly ICustomerRepository _customerRepository;
    private readonly IUnitOfWork _unitOfWork;

    public CustomerService
    (
        ICustomerFactory customerFactory,
        ICustomerRepository customerRepository,
        IUnitOfWork unitOfWork
    )
    {
        _customerFactory = customerFactory;
        _customerRepository = customerRepository;
        _unitOfWork = unitOfWork;
    }

    public async Task<IResult<long>> AddAsync(CustomerModel model)
    {
        var validation = new AddCustomerModelValidator().Validation(model);

        if (validation.Failed) return validation.Fail<long>();

        var customer = _customerFactory.Create(model);

        await _customerRepository.AddAsync(customer);

        await _unitOfWork.SaveChangesAsync();

        return customer.Id.Success();
    }

    public async Task<IResult> DeleteAsync(long id)
    {
        await _customerRepository.DeleteAsync(id);

        await _unitOfWork.SaveChangesAsync();

        return Result.Success();
    }

    public Task<CustomerModel> GetAsync(long id)
    {
        return _customerRepository.GetModelAsync(id);
    }

    public Task<Grid<CustomerModel>> GridAsync(GridParameters parameters)
    {
        return _customerRepository.GridAsync(parameters);
    }

    public async Task<IResult> InactivateAsync(long id)
    {
        var customer = new Customer(id);

        customer.Inactivate();

        await _customerRepository.UpdateStatusAsync(customer);

        await _unitOfWork.SaveChangesAsync();

        return Result.Success();
    }

    public Task<IEnumerable<CustomerModel>> ListAsync()
    {
        return _customerRepository.ListModelAsync();
    }

    public async Task<IResult> UpdateAsync(CustomerModel model)
    {
        var validation = new UpdateCustomerModelValidator().Validation(model);

        if (validation.Failed) return validation;

        var customer = _customerFactory.Create(model);

        await _customerRepository.UpdateAsync(customer);

        await _unitOfWork.SaveChangesAsync();

        return Result.Success();
    }
}

Factory

public sealed class CustomerFactory : ICustomerFactory
{
    public Customer Create(CustomerModel model)
    {
        return new Customer
        (
            model.Id,
            new Name(model.FirstName, model.LastName),
            new Email(model.Email)
        );
    }
}

Domain

Entity

public sealed class Customer : Entity<long>
{
    public Customer(long id) => Id = id;

    public Customer
    (
        long id,
        Name name,
        Email email
    )
    {
        Id = id;
        Name = name;
        Email = email;
        Activate();
    }

    public Name Name { get; private set; }

    public Email Email { get; private set; }

    public Status Status { get; private set; }

    public void Activate() => Status = Status.Active;

    public void Inactivate() => Status = Status.Inactive;
}

ValueObject

public sealed record Name(string FirstName, string LastName);

Model

Model

public sealed record CustomerModel
{
    public long Id { get; init; }

    public string FirstName { get; init; }

    public string LastName { get; init; }

    public string Email { get; init; }
}

ModelValidator

public abstract class CustomerModelValidator : AbstractValidator<CustomerModel>
{
    public void Id() => RuleFor(customer => customer.Id).NotEmpty();

    public void FirstName() => RuleFor(customer => customer.FirstName).NotEmpty();

    public void LastName() => RuleFor(customer => customer.LastName).NotEmpty();

    public void Email() => RuleFor(customer => customer.Email).EmailAddress();
}
public sealed class AddCustomerModelValidator : CustomerModelValidator
{
    public AddCustomerModelValidator() => FirstName(); LastName(); Email();
}
public sealed class UpdateCustomerModelValidator : CustomerModelValidator
{
    public UpdateCustomerModelValidator() => Id(); FirstName(); LastName(); Email();
}

Database

Context

public sealed class Context : DbContext
{
    public Context(DbContextOptions options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.ApplyConfigurationsFromAssembly(typeof(Context).Assembly).Seed();
    }
}

Configuration

public sealed class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
    public void Configure(EntityTypeBuilder<Customer> builder)
    {
        builder.ToTable(nameof(Customer), nameof(Customer));

        builder.HasKey(customer => customer.Id);

        builder.Property(customer => customer.Id).ValueGeneratedOnAdd().IsRequired();

        builder.Property(customer => customer.Status).IsRequired();

        builder.OwnsOne(customer => customer.Name, customerName =>
        {
            customerName.Property(name => name.FirstName).HasColumnName(nameof(Name.FirstName)).HasMaxLength(100).IsRequired();

            customerName.Property(name => name.LastName).HasColumnName(nameof(Name.LastName)).HasMaxLength(200).IsRequired();
        });

        builder.OwnsOne(customer => customer.Email, customerEmail =>
        {
            customerEmail.Property(email => email.Value).HasColumnName(nameof(User.Email)).HasMaxLength(300).IsRequired();

            customerEmail.HasIndex(email => email.Value).IsUnique();
        });
    }
}

Repository

public sealed class CustomerRepository : EFRepository<Customer>, ICustomerRepository
{
    public CustomerRepository(Context context) : base(context) { }

    public Task<CustomerModel> GetModelAsync(long id)
    {
        return Queryable.Where(CustomerExpression.Id(id)).Select(CustomerExpression.Model).SingleOrDefaultAsync();
    }

    public Task<Grid<CustomerModel>> GridAsync(GridParameters parameters)
    {
        return Queryable.Select(CustomerExpression.Model).GridAsync(parameters);
    }

    public async Task<IEnumerable<CustomerModel>> ListModelAsync()
    {
        return await Queryable.Select(CustomerExpression.Model).ToListAsync();
    }

    public Task UpdateStatusAsync(Customer customer)
    {
        return UpdatePartialAsync(new { customer.Id, customer.Status });
    }
}

Expression

public static class CustomerExpression
{
    public static Expression<Func<Customer, CustomerModel>> Model => customer => new CustomerModel
    {
        Id = user.Id,
        FirstName = user.Name.FirstName,
        LastName = user.Name.LastName,
        Email = user.Email.Value
    };

    public static Expression<Func<Customer, bool>> Id(long id) => customer => customer.Id == id;
}
  • 0. 0.1 三层架构、单体架构 0.2 SOA SOA(Service-Oriented Architecture,面向服务架构)是一种架构模型。通过接口发布服务,通过协议通信。 0.3 Web Services 实现SOA的最常见技术标准是Web Services。 Web Services是独立的、模块化的应用,能够通过互联网来描述、发布、定位以及调用。 * SOAP(Simple Obje

  • ARCHITECTURE DAY03 案例1:部署Hadoop 案例2:准备集群环境 案例3:配置Hadoop集群 案例4:初始化并验证集群 案例5:mapreduce模板案例 案例6:部署Yarn 1 案例1:部署Hadoop 1.1 问题 本案例要求安装单机模式Hadoop: 热词分析: 最低配置:2cpu,2G内存,10G硬盘 虚拟机IP:192.168.1.50 hadoop1 安装部署

  • iOS的App现在基本都是用llvm在编译,Xcode也提供了各种设置帮助你进行编译参数的设定。里面有一项就是设定编译的体系结构,涉及到的参数包括:Architectures、Valid Architectures和Build Active Architecture Only。 有些新手对这几个参数往往不知道怎么选择,这里简单介绍一下。 Architectures:这是指你想支持的指令集,比如:a

  • This chapter we will start by explaining two most common approaches to applications architecture; monoliths and microservices Monoliths Application Architecture Monolithic applications are developed a

  • http://highscalability.com/amazon-architecture This is a wonderfully informative Amazon update based on Joachim Rohde's discovery of an interview with Amazon's CTO. You'll learn about how Amazon organ

  •   O Architecture! My Architecture!   1   O ARCHITECTURE! my Architecture! The horrible coding phase is done; The application is running, the functionalities they sought are crunching; The release date

  • AMD64 Architecture The AMD64 architecture is a new 64-bit architecture developed by AMD, based on the 32-bit x86 architecture. It extends the original x86 architecture by doubling the number of genera

  • Architecture pattern: context + problem -> solution Architecture style: solution part of architecture pattern So architecture style is analogous to the solution part of the architecture pattern. It's

  • x64 Architecture         3 out of 3 rated this helpful Rate this topic The x64 architecture is a backwards-compatible extension of x86.  It provides a legacy 32-bit mode, which is identical to x86, an

 相关资料
  • Apache Shiro 设计理念是使程序的安全变得简单直观而易于实现,Shiro的核心设计参照大多数用户对安全的思考模式--如何对某人(或某事)在与程序交互的环境中的进行安全控制。 程序设计通常都以用户故事为基础,也就是说,你会经常设计用户接口或服务api基于用户如何(或应该)与软件交互。 例如,你可能会说,“如果我的应用程序的用户交互是登录,我将展示他们可以单击一个按钮来查看他们的帐户信息。

  • We designed TensorFlow for large-scale distributed training and inference, but it is also flexible enough to support experimentation with new machine learning models and system-level optimizations. Th

  • If you are interested in learning more about how Jest works, what the architecture behind the framework is, and how Jest is split up into individual reusable packages, check out this video:

  • Overview 看ThoughtWorks的技术雷达,里面提到了Micro-Service和Embedded Jetty。大家越来越喜欢用一个嵌入式Jetty加一个嵌入式的H2,不用依赖任何Container,就可以启动一个应用了。 这种嵌入式Embedded有两种风格: 一种是提供一个Zip包,里面有一个bin目录放着jsw(Java Service Wrapper),一个lib目录放着Jet

  • Joomla是一个模型 - 视图 - 控制器Web应用程序。 在本章中,我们将讨论Joomla的架构风格。 下图显示了Joomla的架构。 Joomla的架构包含以下层 - Database Joomla框架 Components Modules Plugin Templates 网络服务器 Database - 数据库是数据的集合,可以以特定方式进行存储,操作和组织。 数据库存储用户信息,内容和

  • JSF技术是用于开发,构建服务器端用户界面组件并在Web应用程序中使用它们的框架。 JSF技术基于模型视图控制器(MVC)架构,用于将逻辑与表示分离。 什么是MVC设计模式? MVC设计模式使用三个独立模块设计应用程序 - S.No 模块和描述 1 Model 携带数据并登录 2 View 显示用户界面 3 Controller 处理应用程序的处理。 MVC设计模式的目的是将模型和表示分开,使开发

  • 第 2 章 体系结构(Architecture)

  • Todo write more here