Providers & Repositories¶
Domain Services is storage and technology-agnostic. The actual persistence or data source is handled by providers, via repository implementations.
Common patterns:
- File-based (JSON/CSV/Directory) usually comes within Domain Services package itself.
- Database providers (e.g., PostgreSQL, MCLite) — install the specific provider package. Some providers don't support all modules yet.
- Remote/data API providers (e.g., USGS/DS) for external sources.
- DFS file-based (dfs0/dfs2/dfsu, etc.) for hydrodynamic and environmental model files.
A service (e.g., ScalarService, TimeSeriesService, JobService) is constructed with a repository that comes from a provider. The same service can work with different repositories—only the repository type and connection string change.
Imperative style (code) vs Declarative style (connections.json)¶
You can register services:
- Imperatively in code with
ServiceLocator.Register(...), or - Declaratively in
connections.json(lazy-loaded at first use).
Both styles support switching providers (JSON ↔ PostgreSQL ↔ external APIs) without changing your controllers.
Example: Scalars with JSON vs PostgreSQL (Imperative)¶
JSON persistence (file-based)
ServiceLocator.Register(
new DHI.Services.Scalars.GroupedScalarService<string, int>(
new DHI.Services.Scalars.ScalarRepository("[AppData]scalars.json".Resolve()),
new SimpleLogger("[AppData]scalars.log".Resolve())
),
"json-scalars"
);
PostgreSQL persistence (production)
// Tip: alias the repository to make the assembly source explicit
using PgScalarRepository = DHI.Services.Provider.PostgreSQL.ScalarRepository;
var postgreSqlScalarsConnectionString = "[env:POSTGRES_SCALARS]";
ServiceLocator.Register(
new ScalarService<int>(
new PgScalarRepository(postgreSqlScalarsConnectionString),
new SimpleLogger("[AppData]scalars.log".Resolve())
),
"pg-scalars"
);
Why alias? Repository types live in different assemblies/namespaces. Being explicit (or aliasing) avoids confusion and makes reviews/maintenance safer.
Example: Scalars with JSON vs PostgreSQL (Declarative via connections.json)¶
Place these entries in App_Data/connections.json. The RepositoryType must be assembly-qualified and match the NuGet package you installed.
JSON provider
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",
"json-scalars": {
"$type": "DHI.Services.Scalars.WebApi.ScalarServiceConnection, DHI.Services.Scalars.WebApi",
"RepositoryType": "DHI.Services.Scalars.ScalarRepository, DHI.Services.Scalars",
"ConnectionString": "[AppData]scalars.json",
"Name": "Scalars (JSON file)",
"Id": "json-scalars"
}
}
PostgreSQL provider
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",
"pg-scalars": {
"$type": "DHI.Services.Scalars.WebApi.ScalarServiceConnection, DHI.Services.Scalars.WebApi",
"RepositoryType": "DHI.Services.Provider.PostgreSQL.ScalarRepository, DHI.Services.Provider.PostgreSQL",
"ConnectionString": "[env:POSTGRES_SCALARS]",
"Name": "Scalars (PostgreSQL)",
"Id": "pg-scalars"
}
}
Running multiple providers side-by-side¶
You can expose several repositories at once—each with a unique connectionId:
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",
"json-scalars": {
"$type": "DHI.Services.Scalars.WebApi.ScalarServiceConnection, DHI.Services.Scalars.WebApi",
"RepositoryType": "DHI.Services.Scalars.ScalarRepository, DHI.Services.Scalars",
"ConnectionString": "[AppData]scalars.json",
"Name": "Scalars (JSON file)",
"Id": "json-scalars"
},
"pg-scalars": {
"$type": "DHI.Services.Scalars.WebApi.ScalarServiceConnection, DHI.Services.Scalars.WebApi",
"RepositoryType": "DHI.Services.Provider.PostgreSQL.ScalarRepository, DHI.Services.Provider.PostgreSQL",
"ConnectionString": "[env:POSTGRES_SCALARS]",
"Name": "Scalars (PostgreSQL)",
"Id": "pg-scalars"
},
"pg-scalars-eu": {
"$type": "DHI.Services.Scalars.WebApi.ScalarServiceConnection, DHI.Services.Scalars.WebApi",
"RepositoryType": "DHI.Services.Provider.PostgreSQL.ScalarRepository, DHI.Services.Provider.PostgreSQL",
"ConnectionString": "[env:POSTGRES_SCALARS_EU]",
"Name": "Scalars (PostgreSQL EU)",
"Id": "pg-scalars-eu"
}
}
At runtime you can call, for example, /api/scalars/pg-scalars/... or /api/scalars/json-scalars/... and each will hit its own backing store.
Choosing a provider¶
| Scenario | Repository type | Pros | Cons |
|---|---|---|---|
| Local dev / demos | JSON/CSV (file) | Zero infra, easy to inspect | Not scalable, no concurrency control |
| Production DB | PostgreSQL, MCLite, etc. | Durable, scalable, transactional | Requires DB, connection management |
| External data source | USGS, DS, etc. | No ETL needed, live data | Rate limits, upstream availability |
In all cases, the service stays the same; only the repository changes.
For a decision guide with per-provider details, see Choosing a Provider.
Package & assembly notes¶
- If you use code registrations, consider type aliasing to make intent explicit:
using JsonScalarRepository = DHI.Services.Scalars.ScalarRepository;
using PgScalarRepository = DHI.Services.Provider.PostgreSQL.ScalarRepository;
Placeholders & secrets¶
Use placeholders to keep config flexible and safe:
[AppData]– local files next to your app (great for dev)[env:VAR_NAME]– inject secrets at deploy time
Example:
"ConnectionString": "[env:POSTGRES_SCALARS]"
Then set POSTGRES_SCALARS in your environment (K8s secret, Azure App Settings, etc.).
Troubleshooting¶
- ”Could not load type …” – The
RepositoryTypestring doesn’t match the installed assembly. Confirm the exact assembly name in your package. - DB errors – Ensure the database exists and credentials are correct. If you don’t have the table, it will be created automatically.
- Multiple providers, same ID – Each
connectionIdmust be unique acrossconnections.jsonandServiceLocator.Register(...). - Auth – If endpoints return
401, use a valid JWT or your dev bypass from earlier sections.
Next: Core Concepts