126 lines
6.1 KiB
Markdown
126 lines
6.1 KiB
Markdown
# KbsV3 Architecture Patterns
|
|
|
|
## 1. Job System (IKnownJob → Worker)
|
|
|
|
### Creating a new Job (full checklist)
|
|
1. **JobType enum** → `GrpcProtos/GrpcServices/Contracts/JobType.cs` — add `[StringValue("X")] X`
|
|
2. **IKnownJob** → `GrpcProtos/GrpcServices/KnownJobs/MyJob.cs` — metadata class
|
|
- `ServiceName` format: `"modulename.WorkerClassName"` (e.g. `"sqlsync.UpdateCalcPriceService"`)
|
|
- Module names: `helpdesk`, `sqlsync`, `ad`, `azure`, `docuware`, `sap`, `scheduler`
|
|
3. **Worker** → `Kbs3XxxService/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-registration** — `IntegrationServiceBase.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
|
|
```csharp
|
|
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
|
|
```bash
|
|
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)`
|