@@ -10,7 +10,7 @@ across both mobile and web platforms. It will cover best practices for structuri
1010platform-specific challenges, and ensuring that your implementation works seamlessly across
1111Bitwarden’s mobile and web applications.
1212
13- ## Architecture
13+ ## Crate structure
1414
1515The internal SDK is structured as a single
1616[ Git repository] ( https://github.com/bitwarden/sdk-internal ) with multiple internal crates. This
@@ -145,6 +145,73 @@ grouped into application interfaces for consumption. See the
145145[ ` VaultClient ` ] ( https://github.com/bitwarden/sdk-internal/blob/main/crates/bitwarden-vault/src/vault_client.rs )
146146as as example.
147147
148+ ## Client structure
149+
150+ One of the core concepts of our SDK is the "client". The client groups the SDK API surface into
151+ domain-specific bundles for easier instantiation and use by the consuming application.
152+
153+ There are two recommended approaches for structuring a client, depending on the size of the domain.
154+
155+ ### Single file
156+
157+ Define the client struct, its initialization, and all method ` impl ` blocks in one file. This
158+ minimizes indirection and keeps related code easy to discover. Prefer this structure when the file
159+ is manageable in size (~ 500 lines, including tests).
160+
161+ ```
162+ domain_client.rs
163+ ├── DomainClient struct definition and initialization
164+ └── impl DomainClient with full method implementations and tests
165+ ```
166+
167+ ### Per-method files or subdirectories
168+
169+ When the single file would otherwise become unwieldy (~ 500 lines, including tests), the client
170+ definition should be split from individual method implementations.
171+
172+ Define the client struct in one file and each method in either its own file or its own subdirectory,
173+ depending on the implementation complexity.
174+
175+ When each method is self-contained and does not require supporting types alongside it, individual
176+ methods can be split into separate files.
177+
178+ ```
179+ domain/
180+ ├── domain_client.rs # DomainClient struct definition and initialization
181+ ├── mod.rs
182+ ├── method_name.rs # impl DomainClient { fn method_name() } and tests
183+ └── other_method.rs # impl DomainClient { fn other_method() } and tests
184+ ```
185+
186+ For more complex clients, subdirectories can be used to contain the ` impl DomainClient ` block for
187+ that method, its tests, and any supporting types.
188+
189+ ```
190+ domain/
191+ ├── domain_client.rs # DomainClient struct definition and initialization
192+ ├── mod.rs
193+ └── method_name/
194+ ├── mod.rs
195+ ├── method_name.rs # impl DomainClient { fn method_name() } and tests
196+ └── request.rs # supporting types (errors, etc.)
197+ ```
198+
199+ ::: warning
200+
201+ Avoid the thin passthrough pattern, where the client delegates to free functions defined elsewhere.
202+ This creates unnecessary indirection and splits documentation away from the API surface.
203+
204+ ``` rust
205+ impl LoginClient {
206+ // Avoid delegating the entire implementation to another function like this.
207+ pub async fn login_with_password (& self , data : LoginData ) -> Result <()> {
208+ login_with_password (self . client, data ). await
209+ }
210+ }
211+ ```
212+
213+ :::
214+
148215## Language bindings
149216
150217The internal SDK supports mobile and web platforms and uses UniFFI and ` wasm-bindgen ` to generate
0 commit comments