claude-meta/memory/projects/kbsV3/kbsv3-architecture.md

6.1 KiB

KbsV3 Architecture Patterns

1. Job System (IKnownJob → Worker)

Creating a new Job (full checklist)

  1. JobType enumGrpcProtos/GrpcServices/Contracts/JobType.cs — add [StringValue("X")] X
  2. IKnownJobGrpcProtos/GrpcServices/KnownJobs/MyJob.cs — metadata class
    • ServiceName format: "modulename.WorkerClassName" (e.g. "sqlsync.UpdateCalcPriceService")
    • Module names: helpdesk, sqlsync, ad, azure, docuware, sap, scheduler
  3. WorkerKbs3XxxService/Services/MyWorker.cs — implements ILongRunningJobService
    • Constructor: ProtocolLogger, IApiComposer, plus custom DI
    • JobType property returns GetJobType() (static method using JobType.X.GetStringValue())
    • Run(JobDataRequest, CancellationToken) → business logic
    • GetHeadline() → section title
  4. Auto-registrationIntegrationServiceBase.AutoInjectServices() discovers workers automatically
  5. No changes needed in Program.cs of the microservice

Worker capabilities

  • ProtocolLogger.LogHeadline/LogInfo/LogWarn/LogError — streams to UI in real-time
  • apiComposer — callback to server (see section 3)
  • request.InputData — string input (usually username or JSON payload)

IKnownJob with Payload

  • Payload property returns a typed object (serialized as InputData)
  • Example: UpdateCalculationPriceDatabase.cs returns UpdateCalcPriceRequest

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)
  • IRfcFunction does 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 string
  • itResult.GetDateTimeOrNull("FIELD") → nullable DateTime
  • itResult.GetDecimal("FIELD"), GetBoolean("FIELD"), etc.

gRPC service wrapper

  • Kbs3SapService/GrpcServices/SapOrderService.cs — thin proxy implementing ISapOrderService
  • 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) or Contracts/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, then var client = await serviceRegistry.GetClient<SapListServiceClient>()
  • Never inject a *Client class 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 (inherits ThreadSafeDbContext)
  • Entities: Kbs3ServerService.Repository/Entities/
  • Base classes: AuditableEntity (manual audit) or KbEntity (auto SQL defaults via GETUTCDATE())
  • Global decimal precision: 18,4
  • Configurations: IEntityTypeConfiguration<T> via ApplyAllConfigurations()
  • Service layer: Kbs3ServerService.Service/Services/ — inject JobDbContext, 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 DBs
  • ConnectionStringKbs = main KBS app DB
  • ConnectionStringSapCalcData = external SAP calc DB
  • Uses SqlConnection + SqlCommand with parameterized queries
  • Pattern: new SqlConnection(connectionString)OpenAsync()ExecuteNonQueryAsync()CloseAsync()
  • Example: UpdateCalcPriceService.cs — fetches via apiComposer, 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 in Services/
  • builder.Services.AddScoped<IFoo, Foo>() in Program.cs

Microservice workers

  • Auto-discovered by IntegrationServiceBase.AutoInjectServices()
  • Keyed service: services.AddKeyedScoped<ILongRunningJobService>(jobType, workerType)