USB to RS232 adapter ICs like the FTDI parts have been one of the most helpful "band-aid" ICs of the modern era. Thanks to baud rates in the hundreds-of-kBPs they are still and will remain to be very useful. That said, there are many advantages in utilizing USB without converting such as higher data-rates and error-checking with the downside being extra complexity in design.
The advantage we have today is that many processors with USB transceivers also have the USB physical layer or "PHY" for USB 2.0 full-speed (FS) and sometimes high-speed (HS) built in which reduces IC count. USB 3.x PHYs generally bump you up to a much higher level of processor which makes it much less practical. You can also assume that said processor manufacturer also has a USB compliant software stack available as well, though some are documented and supported better than others. The cherry on top is that some, such as NXP have a comprehensive catalog of functional code examples to help you hit the ground running. There seem to be a myriad of USB configurations to choose from, but for this discussion, the focus is on simple half-duplex data communications at theoretical maximum rates of up to ~24MBps WITH ERROR DETECTION!
Before I forget, add to your to-do list to buy Jan Axelson's book "USB Complete: The Developer's Guide". If you might ever have to touch USB in your development life, you will want THAT book within reach. With that reference out of the way, there are four USB transfer types: control, bulk, interrupt and isochronous. Control is primarily for USB devices to initiate connection with a host and isochronous does not guarantee delivery of all data. Bulk and interrupt will guarantee data delivery, or at least identify in your code that a data transfer error occurred, but I lean toward interrupt transfers as they have guaranteed latency where bulk has to yield the right-of-way to the other three types. Also, interrupt transfers have a higher theoretical max data rate compared to bulk for both USB 2.0 HS and USB 3.x.
The following is table 2-1 in Jan Axelson's "USB Complete: The Developer's Guide" 5th Edition:
To make things as simple as possible, assuming you will be connecting the device to a Windows or Linux host, you will want to pick a device class that won't require custom drivers. Trying to shorten this long story, of all the USB device classes out there, all of the above led me to the human interface device (HID) class which inherently uses interrupt transfers. The two most common HID devices out there are mice and keyboards which are just a fraction of the types of devices (usage pages) and functions for each of those devices (usages) that are defined within the HID class. For the case of wanting bidirectional data communications for generic purposes HID is still an option as an undefined or vendor specific usage page that plays nice with built-in drivers.
NXP includes example that is simply referred to as a generic HID device. It is part of their SDKs depending the processor. I have only used it with MCUXpresso (ridiculously easy to install the SDK and get up and running) but I believe the SDK examples will work with other IDEs. I have gotten the generic HID code up and running so far with Kinetis, LPC and iMX-RT processor families. Here is a link that explains how I had to modify the code to configure it from USB 2.0 full-speed to high-speed since I wanted the higher data rate and larger packet size. The code in all three cases was VERY similar other than low level hardware configurations which I didn't need to mess with at all.
WARNING - If you are going to send a lot of data in rapid packet succession using the example code in SDK 2.4.x (not sure about 2.5.x), YOU WILL NEED TO MAKE A CHANGE TO THE SEND AND RECEIVE ROUTINES!!! If you don't your program will eventually stop transmitting and be stuck in a race condition that requires a reset. Thankfully, another forum poster blazed this trail. Here's the link.
There is also the HID specific issue that requires "report descriptors" which took me a while to get figured out in terms of understanding their purpose. There are other descriptors but you are better off reading about them in Jan's book referenced above. Here is a link to a great tutorial on report descriptors. The most common examples for dealing with RDs are keyboards and mice and they are very relevant to Windows and Linux drivers, not as much for raw data transfer. Other than ensuring that the total number of bytes of data represented in the RD is equal to the packet size defined elsewhere in the stack configuration, the RD seems to be optional to the host.
That said, being able to define multiple data structures for a given packet size can be very helpful assuming you have an application at the other end that is able to interpret the RD and can't have advance knowledge of what will be coming down the pipe. But note that at the lowest level, data is being transmitted as a sequence of bytes. Once it is received at either end, the data will need to be cast into whatever structure needed by the application. The point is you may define multiple unique reports base on unique data structures you have to transfer if you want; but for simple data transfer you only need one for each direction and some predefined knowledge of the unique structures at the host and device.
I'm definitely NOT and expert in USB, but if you have questions about this particular topic, let me know and I'll try to help.