We also use many classes where we create a DVR for each copy of an object and allow different sections of code to access the same object and directly call public "ByRef" methods. We find that this work well for certain applications. In the future, we will likely change this to the method that you describe where the object itself is passed "By Value" and its private data is a DVR. This will help us with inheritance and dynamic dispatch for certain drivers.
The lack of "Timeout" in the DVR IPE structure is the most frustrating part of that feature to me. During development, those sometimes happen and I would much rather get a specific error code than having my code hanging forever! In that respect, SEQ is superior.
The other issue that we have been facing is the lifetime of the DVR. Keep in mind that it is associated with the Top Level VI in the call chain when the "New Data Value Reference" was created and NOT with the VI that actually created it (such as an FGV used to pass the DVR to different section of code). Therefore, if that Top Level VI goes idle, you will lose your DVR because LabVIEW will automatically clean it up even if many other VI's still access the object and even if the Sub VI that called the "New Data Value Reference" is loaded in memory. This is also valid for TCP/IP connections and any other type of reference. You have to be especially careful with this concept if you tend to Start/Stop VIs while your application is running or at shutdown time to stop your VIs in the right order.
About messaging, we also use that method but I find that being able to call the object's method directly makes it a lot easier to handle sequential commands and error handling that may be happen during the method's execution.