6.1 KiB
6.1 KiB
KbsV3 Architecture Patterns
1. Job System (IKnownJob → Worker)
Creating a new Job (full checklist)
- JobType enum →
GrpcProtos/GrpcServices/Contracts/JobType.cs— add[StringValue("X")] X - IKnownJob →
GrpcProtos/GrpcServices/KnownJobs/MyJob.cs— metadata classServiceNameformat:"modulename.WorkerClassName"(e.g."sqlsync.UpdateCalcPriceService")- Module names:
helpdesk,sqlsync,ad,azure,docuware,sap,scheduler
- Worker →
Kbs3XxxService/Services/MyWorker.cs— implementsILongRunningJobService- Constructor:
ProtocolLogger,IApiComposer, plus custom DI JobTypeproperty returnsGetJobType()(static method usingJobType.X.GetStringValue())Run(JobDataRequest, CancellationToken)→ business logicGetHeadline()→ section title
- Constructor:
- Auto-registration —
IntegrationServiceBase.AutoInjectServices()discovers workers automatically - No changes needed in Program.cs of the microservice
Worker capabilities
ProtocolLogger.LogHeadline/LogInfo/LogWarn/LogError— streams to UI in real-timeapiComposer— callback to server (see section 3)request.InputData— string input (usually username or JSON payload)
IKnownJob with Payload
Payloadproperty returns a typed object (serialized as InputData)- Example:
UpdateCalculationPriceDatabase.csreturnsUpdateCalcPriceRequest
2. SAP Data Access (RFC Calls)
Where RFC calls live
- Kbs3SapService/SapServices/ — e.g.
OrderService.cs,FiService.cs,TableService.cs - Uses SAP NCo3:
RfcDestinationManager.GetDestination(systemName)→repos.CreateFunction("Z_FUNC")→fnc.Invoke(dest) IRfcFunctiondoes NOT implement IDisposable
RFC call pattern
var dest = RfcDestinationManager.GetDestination(request.SystemName);
var repos = dest.Repository;
var fnc = repos.CreateFunction("Z_RFC_NAME");
fnc.SetValue("PARAM", value); // importing params
fnc.Invoke(dest);
var itResult = fnc.GetTable("ET_TABLE"); // exporting table
for (var i = 0; i < itResult.RowCount; i++) {
itResult.CurrentIndex = i;
var val = itResult.GetString("FIELD");
}
Extension methods (Kbs3SapService/Extensions/)
DateTime.ToSapDate()→ SAP date stringitResult.GetDateTimeOrNull("FIELD")→ nullable DateTimeitResult.GetDecimal("FIELD"),GetBoolean("FIELD"), etc.
gRPC service wrapper
Kbs3SapService/GrpcServices/SapOrderService.cs— thin proxy implementingISapOrderService- Delegates to the actual
OrderService(SapServices layer)
3. gRPC Communication Between Modules
Contract interfaces
- Location:
GrpcProtos/GrpcServices/Contracts/SapService/(e.g.ISapOrderService.cs) - Attributes:
[ServiceContract],[OperationContract] - DTOs:
[DataContract]+[DataMember(Order = N)]
DTO conventions
- Namespace:
GrpcProtos.GrpcServices.Model(data classes) orContracts/SapService/(request/response) - Collections:
Type[] prop = [] - Nullable refs:
string?,DateTime? - Sequential
[DataMember(Order = N)]starting at 1 BapiMessage[]for error handling
Server → Microservice (gRPC Client)
- Location:
Kbs3ServerService/GrpcClients/(e.g.SapOrderServiceClient.cs) - Inherits
GrpcClientBase<ISapOrderService> - Constructor:
(GrpcChannel, ServiceRegistry, IServiceScopeFactory) - Methods:
return Client?.MethodName(request); - NOT registered in DI — resolved via
ServiceRegistry.GetClient<T>()(async) - To use in a service: inject
ServiceRegistry, thenvar client = await serviceRegistry.GetClient<SapListServiceClient>() - Never inject a
*Clientclass directly via constructor DI — it will throw at startup
Microservice → Server (IApiComposer callback)
- Interface:
GrpcProtos/GrpcServices/Contracts/Server/IApiComposer.cs - Implementation: in Kbs3ServerService (ApiComposer class)
- Workers use
apiComposer.GetUser(),apiComposer.GetCalcPriceInfos(), etc. - To add new callback: add method to IApiComposer → implement in server ApiComposer
4. Database Read/Write
Server-side (EF Core)
- DbContext:
Kbs3ServerService.Repository/Contexts/JobDbContext.cs(inheritsThreadSafeDbContext) - Entities:
Kbs3ServerService.Repository/Entities/ - Base classes:
AuditableEntity(manual audit) orKbEntity(auto SQL defaults via GETUTCDATE()) - Global decimal precision: 18,4
- Configurations:
IEntityTypeConfiguration<T>viaApplyAllConfigurations() - Service layer:
Kbs3ServerService.Service/Services/— injectJobDbContext, use EF Core CRUD
Migrations
dotnet ef migrations add Name --project Kbs3ServerService.Repository --startup-project Kbs3ServerService
dotnet ef database update -- Development --project Kbs3ServerService.Repository --startup-project Kbs3ServerService
Microservice-side (Raw ADO.NET) — Kbs3SqlService pattern
IOptions<DatabaseOptions>→ connection strings for multiple DBsConnectionStringKbs= main KBS app DBConnectionStringSapCalcData= external SAP calc DB- Uses
SqlConnection+SqlCommandwith parameterized queries - Pattern:
new SqlConnection(connectionString)→OpenAsync()→ExecuteNonQueryAsync()→CloseAsync() - Example:
UpdateCalcPriceService.cs— fetches viaapiComposer, writes via raw SQL
Key difference
| Location | Technology | Use case |
|---|---|---|
| Kbs3ServerService | EF Core (JobDbContext) | Main app CRUD, entities, migrations |
| Kbs3SqlService | Raw ADO.NET (SqlConnection) | Bulk sync to external/app DBs from workers |
| Kbs3SapService | None | No direct DB access — SAP only |
5. Service Registration
Server services
- Interface in
Contract/, implementation inServices/ builder.Services.AddScoped<IFoo, Foo>()inProgram.cs
Microservice workers
- Auto-discovered by
IntegrationServiceBase.AutoInjectServices() - Keyed service:
services.AddKeyedScoped<ILongRunningJobService>(jobType, workerType)