Hmm, this causes some other design issues. We will have a SnakSerializer which has to take a $dataValueSerializer. Of course we should not specify a concrete type because of flexibility, but a problem of this solution is that wrong Serializers that cannot handle DataValues will only throw an exception at runtime but do not show a warning when "compiling". Do you have any suggestions how we can avoid those issues?

Good observation. This, a decrease in type safety, is a typical trade-off one makes when creating boundaries. This means the code constructing a SnakSerializer is responsible for making sure it is feeding in a Serializer implementation that can serialize data values. A small price to pay for the decoupling and flexibility gained.

Ok, this sounds reasonable.
Also note that not a lot of code should be constructing these service objects, and know about the dependencies. In fact, this likely should only be done in one place, which is a factory provided by the library. An example of this can be seen here: https://github.com/wmde/AskSerialization/blob/master/src/Ask/DeserializerFactory.php
Thanks for the example. I think this will be the next step after we have finished the serializers and deserializers.
Another important thing to realize is that if one writes the right component and integration tests, any incorrect wiring up of the serializes will be found.
I see that if we write good tests all problems with types will be found at least when running the tests. This sounds very reasonable. Thank you for your explanations. :-)

Best regards,