Domain Objects (DTOs)
Latency-sensitive paths: never use DTOs. Domain objects allocate (
Vec,String) and copy every field out of the wire buffer. If latency matters, use the zero-copy flyweight decoder instead. DTOs are for tooling, logging, and offline processing — not the hot path.
DTO construction still owns and allocates its Vec/String fields. Re-encode
does not add another allocation: wire-compatible flat groups automatically use
the generated bulk_add_domain(&[EntryDomain]) path, which validates one
complete output region and writes directly from the DTO slice. Groups with
nested tails, var-data, optional/versioned fields, domain conversions, or bool
domain remapping retain the general per-entry path.
Enable domain objects during generation when an owned application value is
more convenient than a zero-copy flyweight. This fixture uses DomainVarData::Strings:
#![allow(unused)]
fn main() {
pub fn demo_car_domain_dto(wire: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
let dec = CarDecoder::try_decode(wire, 0)?;
// try_from_decoder (not TryFrom/From): two fallible sources — decoder vs
// try_from_slice_with_header for framed bytes; materialisation can fail.
let dto = CarDomain::try_from_decoder(dec)?;
assert_eq!(dto.serial_number, 1234);
assert!(dto.available); // bool domain field
assert_eq!(dto.fuel_figures.len(), 2);
assert_eq!(dto.fuel_figures[0].usage_description, "Urban");
assert_eq!(dto.manufacturer, "Honda");
const RE_PAD: usize = 512;
assert!(wire.len() <= RE_PAD);
let mut storage = [0u8; RE_PAD];
let n = dto.encode(&mut storage[..wire.len()])?;
assert_eq!(&storage[..n], wire, "DTO re-encode must be byte-identical");
Ok(())
}
}
(This code comes from the sbe-feature-tour sample crate.)