Microservice Solution: Adding New Applications

You must have an ABP Business or a higher license to be able to create a microservice solution.

You can add new applications to the microservice solution template. This document explains how to add new web applications to the solution. In the solution template, there is a folder named apps in the root directory, which contains all the applications in the solution. You can create separate applications for different purposes, such as web applications, public websites for landing pages, or admin panels.

Additionally, there is a folder named _templates in the root directory. This folder contains templates you can use to create new microservices, API gateways, and applications. These templates can be customized according to your needs.

Adding a New Web Application

To add a new web application to the solution, you can use the web template. This template creates a new ASP.NET Core application with the necessary configurations and dependencies. Follow the steps below to add a new web application:

In ABP Studio Solution Explorer, right-click on the apps folder and select Add -> New Module -> Web.

new-web-application

It opens the Create New Module dialog. Enter the name of the new application, specify the output directory if needed, and click the Next button. There is a naming convention: the Module name should include the solution name as a prefix, and the use of the dot (.) character in the Module name is not allowed.

create-new-web-app-module

Select the UI framework and click the Next button.

create-new-module-ui-framework

Select the UI theme and click the Create button.

create-new-module-ui-theme

The new application is created and added to the solution. You can see the new application in the apps folder.

public-web-app

Configuring the appsettings.json

The new application is created with the necessary configurations and dependencies. We should configure the appsettings.json file to set the Authority section for authentication and the Gateway section for the API gateway. You can copy the configurations from the existing web application and modify them according to the new application. Below is an example of the appsettings.json file for the Public web application.

{
  "App": {
    "SelfUrl": "http://localhost:44344",
    "EnablePII": false
  },
  "AuthServer": {
    "Authority": "http://localhost:44387",
    "RequireHttpsMetadata": "false",
    "ClientId": "WebPublic",
    "ClientSecret": "1q2w3e*",
    "IsOnK8s": "false",
    "MetaAddress": "http://localhost:44387"
  },
  "RemoteServices": {
    "Default": {
      "BaseUrl": "http://localhost:44333"
    }
  },
  ...
}

Configuring the OpenId Options

You can use existing OpenIddict applications or create a new one for the new web application. In our example, we use WebPublic as the ClientId and 1q2w3e* as the ClientSecret. According to the configurations in the appsettings.json file, we should configure the OpenIddictDataSeeder class in the Identity service. You can copy the configurations from the existing web application and modify them according to the new application. Below is an example of the OpenIddictDataSeeder configuration for the Public web application.

private async Task CreateClientsAsync()
{
    ...

    //Web Public Client
    var webPublicClientRootUrl = _configuration["OpenIddict:Applications:WebPublic:RootUrl"]!.EnsureEndsWith('/');
    await CreateOrUpdateApplicationAsync(
        name: "WebPublic",
        type: OpenIddictConstants.ClientTypes.Confidential,
        consentType: OpenIddictConstants.ConsentTypes.Implicit,
        displayName: "Web Public Client",
        secret: "1q2w3e*",
        grantTypes: new List<string> //Hybrid flow
        {
            OpenIddictConstants.GrantTypes.AuthorizationCode,
            OpenIddictConstants.GrantTypes.Implicit
        },
        scopes: commonScopes.Union(new[]
        {
            "AuthServer", 
            "IdentityService",
            "SaasService",
            "AuditLoggingService",
            "AdministrationService"
        }).ToList(),
        redirectUris: new List<string> { $"{webPublicClientRootUrl}signin-oidc" },
        postLogoutRedirectUris: new List<string>() { $"{webPublicClientRootUrl}signout-callback-oidc" },
        clientUri: webPublicClientRootUrl,
        logoUri: "/images/clients/aspnetcore.svg"
    );
}

Add the new application URL to the appsettings.json file in the Identity service.

{
  "OpenIddict": {
    "Applications": {
      ...
      "WebPublic": {
        "RootUrl": "http://localhost:44344"
      }
    }
  }
}

Docker Configuration for Prometheus

If you want to monitor the new application with Prometheus when you debug the solution, you should add the new application to the prometheus.yml file in the etc/docker/prometheus folder. You can copy the configurations from the existing applications and modify them according to the new application. Below is an example of the prometheus.yml file for the WebPublic application.

  - job_name: 'webpublic'
    scheme: http
    metrics_path: 'metrics'
    static_configs:
    - targets: ['host.docker.internal:44344']

Creating Helm Chart for the New Application

If you want to deploy the new application to Kubernetes, you should create a Helm chart for the new application.

First, add the new application to the build-all-images.ps1 script in the etc/helm folder. You can copy the configurations from the existing applications and modify them according to the new application. Below is an example of the build-all-images.ps1 script for the WebPublic application.

./build-image.ps1 -ProjectPath "../../apps/web-public/Acme.Bookstore.WebPublic/Acme.Bookstore.WebPublic.csproj" -ImageName bookstore/webpublic

Since we want to expose our application outside the cluster, we should add the host URL to the values.projectname-local.yaml file in the etc/helm/projectname folder. Below is an example of the values.bookstore-local.yaml file for the WebPublic application.

global:
  ...
  hosts:
    ...
    webpublic: "[RELEASE_NAME]-webpublic"

For development purposes, we should also create TLS certificates for the new application. You can edit the create-tls-certificate.ps1 script in the etc/helm folder to generate TLS certificates for the new application. Below is an example of the create-tls-certificate.ps1 script for the WebPublic application.

mkcert --cert-file bookstore-local.pem --key-file bookstore-local-key.pem "bookstore-local" ... "bookstore-local-webpublic"
kubectl create namespace bookstore-local
kubectl create secret tls -n bookstore-local bookstore-local-tls --cert=./bookstore-local.pem --key=./bookstore-local-key.pem

Lastly, we should define the new application in the _helpers.tpl file in the etc/helm/projectname/templates folder. You can copy the configurations from the existing applications and modify them according to the new application. Below is an example of the _helpers.tpl file for the WebPublic application.

{{- define "bookstore.hosts.webpublic" -}}
{{- print "https://" (.Values.global.hosts.webpublic | replace "[RELEASE_NAME]" .Release.Name) -}}
{{- end -}}

Afterwards, we need to create a new Helm chart for the new application. You can copy the configurations from the existing applications and modify them according to the new application. Below is an example of the webpublic Helm chart for the WebPublic application.

# values.yaml
image:
  repository: "bookstore/webpublic"
  tag: "latest"
  pullPolicy: IfNotPresent
authServer:
  clientSecret: "1q2w3e*"

# Chart.yaml
apiVersion: v2
name: webpublic
appVersion: "1.0"
description: Bookstore Web Public Application
version: 1.0.0
type: application

# webpublic.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
spec:
  selector:
    matchLabels:
      app: "{{ .Release.Name }}-{{ .Chart.Name }}"
  template:
    metadata:
      labels:
        app: "{{ .Release.Name }}-{{ .Chart.Name }}"
    spec:
      containers:
      - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: "{{ .Values.image.pullPolicy }}"
        name: "{{ .Release.Name }}-{{ .Chart.Name }}"
        ports:
        - name: "http"
          containerPort: 80
        env:
        - name: "DOTNET_ENVIRONMENT"
          value: "{{ .Values.global.dotnetEnvironment }}"
        - name: "App__SelfUrl"
          value: "{{ include "bookstore.hosts.webpublic" . }}"
        - name: App__EnablePII
          value: "{{ .Values.global.enablePII }}"
        - name: "AuthServer__Authority"
          value: "{{ include "bookstore.hosts.authserver" . }}"
        - name: "AuthServer__ClientSecret"
          value: "{{ .Values.authServer.clientSecret }}"
        - name: "AuthServer__IsOnK8s"
          value: "true"
        - name: "AuthServer__MetaAddress"
          value: "http://{{ .Release.Name }}-authserver"
        - name: "RemoteServices__Default__BaseUrl"
          value: "http://{{ .Release.Name }}-webgateway"
          ...

# webpublic-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    name: "{{ .Release.Name }}-{{ .Chart.Name }}"
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
spec:
  ports:
    - name: "80"
      port: 80
  selector:
    app: "{{ .Release.Name }}-{{ .Chart.Name }}"

# webpublic-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: "{{ .Release.Name }}-{{ .Chart.Name }}"
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: "/"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
    nginx.ingress.kubernetes.io/proxy-buffers-number: "8"
    cert-manager.io/cluster-issuer: "letsencrypt"
spec:
  ingressClassName: "nginx"
  tls:
  - hosts:
      - "{{ (include "bookstore.hosts.webpublic" .) | trimPrefix "https://" }}"
    secretName: "{{ .Values.global.tlsSecret }}"
  rules:
  - host: "{{ (include "bookstore.hosts.webpublic" .) | trimPrefix "https://" }}"
    http:
      paths:
      - path: /
        pathType: "Prefix"
        backend:
          service:
            name: "{{ .Release.Name }}-{{ .Chart.Name }}"
            port:
              number: 80

After creating the Helm chart, you can Refresh Sub Charts in the ABP Studio.

kubernetes-refresh-sub-charts

Then, update Metadata information right-click the application sub-chart, select Properties it open Chart Properties window. You can edit in the Metadata tab.

application-chart-properties

Add the service name Regex pattern Kubernetes Services in the Chart Properties -> Kubernetes Services tab.

application-chart-properties-kubernetes-services

Last but not least, we need to configure the helm chart environments for identity microservice.

# identity.yaml 
# Add this line to the "env:" section
- name: "OpenIddict__Applications__WebPublic__RootUrl"
  value: "{{ include "bookstore.hosts.webpublic" . }}"

Customizing the Application Template

You can customize the application template if needed. Add new configurations, dependencies, or modules to the template by opening the _templates folder in the root directory and then the web folder. Modify the web template as required. The naming convention dictates that microservicename represents the name of the application when created. Use microservicename in the template files for dynamic naming. In the web folder, there are 3 subfolders: Blazor, BlazorServer, and MVC. You can customize the template according to the UI framework you selected when creating the new application.


Contributors


Last updated: September 02, 2024 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Community Talks

Building Modular Monolith Applications Using .NET and ABP Framework

17 Oct, 17:00
Online
Watch the Event
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.

Learn More