---
title: Architecture
summary: How the proxy is constructed, configured, invoked, and disposed.
status: draft
owner: SAIF.Core documentation
last_reviewed: 2026-09-24
---

# Architecture

This repository reaches `SAIFFaxService` through the generated `FaxServiceSoapClient`, wrapped by the authored `FaxServiceProxy`, which builds its own Windows-credentialed `BasicHttpBinding` and `EndpointAddress` in code rather than relying on the `app.config` endpoint.

## Proxy mechanism

| Aspect | Finding |
| --- | --- |
| Mechanism | Service Reference (svcutil) |
| Proxy type | `SAIF.Services.Web.Proxies.Fax.FaxServiceSoapClient` (`internal`) |
| Base type | `System.ServiceModel.ClientBase<T>` |
| Contract type | `internal interface FaxServiceSoap` |
| Constructed in | `FaxService.SendFax` constructor path (`Services/SAIF.Services.WCF/Services/FaxService.cs:21-25`, wrapper constructed with the resolved URL) |
| Binding configured in | Code |
| Address supplied by | Constructor argument (`serviceUrl`), itself sourced from `EnvironmentConfigKeys.FaxSoapServiceUrl` (`faxSoapServiceUrl`) |

## Architecture diagram

The following diagram visualizes how a call reaches the remote service.

```mermaid
flowchart TD
    CALLER["FaxService.SendFax"] --> WRAP["FaxServiceProxy<br>hand-written wrapper"]
    WRAP --> BIND["Binding constructed<br>BasicHttpBinding<br>TransportCredentialOnly, Windows"]
    BIND --> ADDR["EndpointAddress<br>from faxSoapServiceUrl setting"]
    ADDR --> PROXY["FaxServiceSoapClient<br>generated proxy"]
    PROXY --> REMOTE["Remote service<br>SAIFFaxService (ASMX)"]
    WRAP --> DISPOSE["Close only<br>no Abort on fault"]
```

## Hand-written wrapper

| Field | Value |
| --- | --- |
| **Type** | `SAIF.Services.Web.Proxies.FaxServiceProxy` |
| **Source** | `Services/SAIF.Services.Web.Proxies/FaxServiceProxy.cs` |
| **Visibility** | `public` |
| **Implements** | `IDisposable` |
| **Responsibility** | Validates the supplied service URL, constructs the generated `FaxServiceSoapClient` with a Windows-credentialed binding and an explicit address, and exposes `SendFax` |
| **Proxy lifetime** | Per instance — constructed from `OperationContext.Current` in the `FaxService` constructor, so it is per-instance and not reused* (depends on the WCF service's undeclared `InstanceContextMode`) |

| Configuration applied | Value | Source |
| --- | --- | --- |
| `BasicHttpBinding` security mode | `TransportCredentialOnly` | `FaxServiceProxy.cs:18` |
| Transport client credential | `HttpClientCredentialType.Windows` | `FaxServiceProxy.cs:28` |
| `MaxReceivedMessageSize` | `int.MaxValue` | `FaxServiceProxy.cs:20` |
| `MaxBufferSize` | `int.MaxValue` | `FaxServiceProxy.cs:21` |
| `ReaderQuotas.MaxArrayLength` | `int.MaxValue` | `FaxServiceProxy.cs:22` |
| Timeouts | None set — binding defaults apply | `FaxServiceProxy.cs:18-28` |

The response message is empty, so the practical exposure of the unbounded quotas is small, but the setting is copied boilerplate rather than a deliberate choice for this operation — see [Tech debt](tech-debt.md).

## Endpoint resolution

| Environment | Address | Resolved from | Changeable without a rebuild |
| --- | --- | --- | --- |
| All (dev1, dev2, test1, test2, qa, prod) | `http://websvcprod/webservices/SAIFFaxService/faxservice.asmx` | `Services/SAIF.Services.WebApi/EnvironmentApi/App_Data/environments.xml:16,35,54,73,91,109` | Yes — the value is read from `environments.xml` at runtime, but every track currently points at the same production host |

Three different hosts appear across the repository: the runtime value is `websvcprod` (`environments.xml`), the generation-time WSDL/disco/svcmap value is also `websvcprod`, and the unused `app.config` endpoint (`Services/SAIF.Services.Web.Proxies/app.config:60`) names `api.saif.com`. The disagreement is recorded as a finding with no reconciliation attempted.

## Lifetime and disposal

| Aspect | Finding |
| --- | --- |
| Proxy lifetime | Per instance* — see the wrapper table above |
| Disposal | `Dispose()` calls `webService.Close()` (`FaxServiceProxy.cs:57-65`); there is no `Abort()` fallback |
| Faulted-channel handling | `Close()` on a faulted `ClientBase<T>` throws `CommunicationObjectFaultedException` during dispose* |
| Thread safety | Not determined from source |

## Integration points

| System | Type | Purpose |
| --- | --- | --- |
| `SAIFFaxService` | SOAP service | Delivers a fax document to a named recipient and fax number |

## Related documentation
- [Client index](index.md)
- [Overview](overview.md)
- [Contract](contract.md)
- [Operation map](operation-map.md)
- [Client reference](reference.md)
- [Consumption](consumption.md)
- [Dependencies](dependencies.md)
- [Tech debt](tech-debt.md)

> **Note:** Statements marked with `*` were inferred from code structure or contextual
> evidence.

## Sources
- `Services/SAIF.Services.Web.Proxies/FaxServiceProxy.cs`
- `Services/SAIF.Services.WCF/Services/FaxService.cs`
- `Services/SAIF.Services.Web.Proxies/app.config`
