Project: The Update Framework (TUF) (Provisioning / Security & Compliance)
File: 0006-where-to-implemenent-model-serialization.md

Technical Story: https://github.com/theupdateframework/python-tuf/pull/1279 In the course of implementing a class-based role metadata model we have also reviewed options on how to design serialization infrastructure between wire formats and the class model. In an initial attempt we have implemented serialization on the metadata class (see option 1), but issues with inheritance and calls for more flexibility have caused us to rethink this approach. A class-based role metadata model (see ADR4) requires serialization routines from and to wire formats TUF integrators may require custom serialization implementations for custom wire formats Readability and simplicity of implementation for users and maintainers Recognizability of specification Serialization in metadata classes Serialization in metadata subclasses Serialization separated from metadata classes Compromise 1: Default serialization methods in metadata classes / non-default serialization separated Compromise 2: Serialization separated / dict conversion helper methods for default serialization in metadata classes Chosen option: "Compromise 2", because implementing dict conversion as methods on a corresponding class is idiomatic and allows for well-structured code. Together with a separated serialization interface, it provides both ease of use and maintenance, and full flexibility with regards to custom serialization implementations and wire formats. Serialization is implemented on metadata classes, e.g. `Metadata.serializeasjson()`, etc. Good, because serialization for any object is encapsulated within the corresponding class and thus structured in small code chunks, using the already existing hierarchical class model structure. Good, because the TUF specification is heavily based on json, even if only for illustrative purposes, thus this option facilitates recognizability. Bad, because it might suggest that TUF is limited to json alone. Bad, because it does not facilitate custom serialization implementations. Bad, because it can get complicated with inheritance in the class model. NOTE: a workaround exists in #1279. Serialization is implemented on metadata subclasses, e.g. `JsonMetadata.serialize()`, etc. Good, because the wire format is decoupled from the base classes, not giving the impression that TUF is limited to json, and facilitating custom

Bad, because a user needs to decide on serialization ahead of time, when instantiating the metadata objects. Bad, because the metadata model has many classes, which would all need to be subclassed accordingly. Serialization is implemented independently of the metadata class, e.g. by defining an abstract `Serializer` interface, which must be implemented in subclasses, e.g. `JsonSerializer`, etc. Good, because the wire format is completely decoupled from the class model, not giving the impression that TUF is limited to json, and facilitating custom implementations. Good, because it can serve as exact blueprint for custom implementations. Bad, because a decoupled serialization implementation needs to "re-implement" the entire class hierarchy, likely in a procedural manner. Default json serialization is implemented on the metadata class as described in (1), but can be overridden using an independent `Serializer` interface as described in (3). Good, for the reasons outlined in options (1) and (3), i.e. encapsulation within classes but decoupled class model and wire format. Bad, because it creates two different code paths for default and non-default wire formats making the code more complex and prone to deteriorate, especially on the non-default path. Bad, because the on-the-class default implementation can not be used as blueprint for custom implementations. Serialization is implemented independently of the metadata class as described in (3). However, the meat of the default `JsonSerializer`, i.e. conversion between metadata objects and dicts, is implemented on the metadata class, e.g. as `Metadata.to_dict()`, etc. Good, for the reasons outlined in options (1) and (3), i.e. encapsulation within classes but decoupled class model and wire format, without the disadvantage in (4) of having two completely different code paths. Good, because it makes the separate default serializer a minimal wrapper around the dict conversion methods. Good, because other serialization implementations might also make use of dict conversion methods. Good, because conversion between class objects and dicts is akin to type casting, which is idiomatic to implement on the class. Bad, because the on-the-class default implementation can not be used as blueprint for custom implementations. - -