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.)