Prerequisites
- A development environment. This is our recommendation:
- Windows 10
- Visual Studio at least 2019 Professional
- If you install it, make sure that you select Workloads:
Azure development
- If you install it, make sure that you select Workloads:
- .NET Core SDK (at least 2.2)
Get started
Clone the Nexus Code Examples repositry and copy the NetCoreBusinessApi
folder to a new workspace of your choice.
Go through the repository and rename all references from Acme
to the name of the organization.
Authenticate to Nexus
You should never store secrets with your code. By following the steps below, you will be using a local file on your PC for your secrets, see Secret Manager tool.
- Right click on your
Service
project, selectManage User Secrets
- Add your Nexus autentication:
{
"Authentication": {
"Nexus": {
"Endpoint": "https://prdsim-fulcrum-fundamentals.azurewebsites.net",
"ClientId": "tst-business-api",
"ClientSecret": "xxxxxxxxxxxx"
},
"Local": {
"Endpoint": "https://prdsim-fulcrum-fundamentals.azurewebsites.net",
"ClientId": "business-api",
"ClientSecret": "xxxxxxxxxxxx"
}
}
Create a capability
Now you are ready to prepare your first capability. The business capabilities are declared in the Business Api so that they can later be implemented in an adapter. The capabablity has some different parts that are all declared by contracts and controllers. These are then published in the Nuget-packages for your organization that the adapters will consume.
Contract
The contracts are defined for each capability in the Nuget for Libraries. Under the folder libraries in your Business API there are contracts for capabilities and events
Capability contracts
The contract is defined with a model in C#. The model for invoicing looks like this in the example repo:
/// <summary>
/// Data type for the entity Invoice
/// </summary>
public class Invoice : IUniquelyIdentifiable<string>
{
/// <inheritdoc />
public string Id { get; set; }
/// <summary>
/// The total amount for this invoice
/// </summary>
public double TotalAmount { get; set; }
/// <summary>
/// When the invoice was created, i.e. the time when it can be accounted for.
/// </summary>
public DateTime IssuedAtUtcTime { get; set; }
/// <summary>
/// When the invoice expires, i.e. the latest time that it has to be paid
/// </summary>
public DateTime ExpiresAtUtcTime { get; set; }
/// <summary>
/// The actual time when the invoice was paid, or at least when our organization noted it as paid
/// </summary>
public DateTime? PaidAtUtcTime { get; set; }
}
Then create an interface that inherits from ICrud and create a service with the methods you want for your invoice capability.
/// <summary>
/// The services
/// </summary>
public interface IInvoicingCapability : ICrudable<Invoice, string>
{
/// <summary>
/// The service for <see cref="Invoice"/>.
/// </summary>
IInvoiceService InvoiceService { get; set; }
}
And the service
/// <summary>
/// Service for invoices
/// </summary>
public interface IInvoiceService : IRead<Invoice, string>, IReadAllWithPaging<Invoice, string>, ICreateWithSpecifiedId<Invoice, string>
{
/// <summary>
/// Read all invoices that have passed their date of
/// </summary>
/// <param name="descending">True means that the result is sorted with the oldest late invoice first,
/// false with the newest late invoice first.</param>
/// <param name="offset"></param>
/// <param name="limit"></param>
/// <param name="token">Propagates notification that operations should be canceled.</param>
/// <returns>A paged list with the late invoices.</returns>
Task<PageEnvelope<Invoice>> ReadAllLateWithPagingAsync(bool descending = false, int offset = 0, int? limit = null, CancellationToken token = default(CancellationToken));
}
}
With ICrud we now have created all the contracts for all the endpoints we need for the invocing capability.
Event contracts
We also need to specify the contracts for the events. This way the adapters will be able to publish those.,
The event needs to inherit from IPublishableEvent
/// <summary>
/// This event is published whenever a new Invoices has been published.
/// </summary>
public class InvoiceWasIssuedEvent : IPublishableEvent
{
/// <inheritdoc />
public EventMetadata Metadata { get; set; } = new EventMetadata("Invoice", "Paid", 1, 0);
/// <summary>
/// The id of the invoice
/// </summary>
public string InvoiceId { get; set; }
/// <summary>
/// The total amount for this invoice.
/// </summary>
public double TotalAmount { get; set; }
/// <summary>
/// When the invoice was created, i.e. the time when it can be accounted for.
/// </summary>
public DateTime IssuedAtUtcTime { get; set; }
}
It is not enough for the adapter to only publish an event with this contract when it is created. The adapter also need to be registered as a publisher in Business Events Service. This is done by your ICC.
Controllers
Next step is to create the controllers for the capabilities. The controllers are also defined from the Business API to the nugets and then implemented from the adapters.
To create the controllers for Read of the Invoice we only need to inherit the Interface we created above.
/// <summary>
/// Service implementation of <see cref="IInvoiceService"/>
/// </summary>
[Route("api/Economy/v1/[controller]")]
[ApiController]
public abstract class InvoicesControllerBase : IInvoiceService
{
/// <summary>
/// The capability for this controller
/// </summary>
protected readonly IInvoicingCapability Capability;
/// <summary>
/// The CrudController for this controller
/// </summary>
protected readonly ICrud<Invoice, string> CrudController;
/// <summary>
/// Constructor
/// </summary>
/// <param name="capability">The logic layer</param>
protected InvoicesControllerBase(IInvoicingCapability capability)
{
Capability = capability;
CrudController = new CrudControllerHelper<Invoice>(capability.InvoiceService);
}
/// <inheritdoc />
[HttpGet("{id}")]
[Authorize(Roles = "business-api-caller")]
public virtual Task<Invoice> ReadAsync(string id, CancellationToken token = default(CancellationToken))
{
return CrudController.ReadAsync(id, token);
}
}
Register the capability
Now you need to register the capability in the startup of the Business API
// Invoicing
var invoicingUrl = FulcrumApplication.AppSettings.GetString("InvoiceCapability.Url", true);
services.AddScoped<IInvoicingCapability>(provider =>
ValidateDependencyInjection(provider, p =>
new InvoicingCapability($"{invoicingUrl}", httpClient, GetLocalCredentials())));
This way the capability can be consumed from the Business API. When you register your capability you also provide the url and credentials to connect to the adapter. This way the calls to the Business API is forwarded to the correct adapter.
It is important that all the consumers of the capabilities are calling the Business API and not the adapters. This way the system and the implementations of the capabilties can change, but all the consumers don't notice anything.
Publish the nugets
Now you can also publish the new version of the nugets to your organizations Nuget feed.
Then we are ready to implement the nuget from the Nexus adapter in the See the Nexus adapter tutorial.