Is there a way to hold back water sources without using a bunch of ugly signs or fence gates?
using signs and fence gates is ugly, is there any other way to do it?
There are three ways - differing in difficulty and robustness. Unfortunately for your case (3x3 surface) only the last, which is really hard, would suffice - but if you're willing to "scale back" a bit, you can achieve decent effects on smaller budget.
First, the easiest - water source block finds the "nearest hole" when placed, and flows towards it (or splits if there are more within same distance). It scans the area up to 5 blocks away, but only when updated - placed, or something is placed or removed adjacent to it. It doesn't check for other terrain changes. So if you trigger such directional flow, you can remove blocks around that provided "path of greater resistance" and have water "hanging over the edge:
The second one is based upon a glitch, where a retracting piston head that fails to pull a slime block structure doesn't update adjacent water. In the below example, the process is in two steps, first four pistons fail to move the structure blocked by obsidian blocks, then after removing them, I can add a second structure, then retract the last piston head:
This technique allows for quite fancy 'floating water' features, and quite a bit of flexibility, but the necessity for the piston on one side, the slime block structure on the other, and the fact removing them updates the water, limits applications in case of wider streams.
Then we come to expert techniques. They are way too complex to describe in a simple Q&A answer - there's block update suppression, which can be used to remove temporary blocks holding water in place, without updating the water, chunk savestating which can be used to erase any changes within a chunk (including water flowing into it from water sources) without updating blocks in neighboring chunks, and modded features like Carpet Mod's fillUpdates rule, after which you can perform a /fill command that won't update adjacent blocks (and e.g. erase flowing water).
In your case, though, I believe the most viable approach is to accept that water will flow to the sides - and 'sink' it in waterlogged blocks:
You can use half slabs, which are more easily integrated with the surrounding environment. Trap doors can also serve the same purpose