There's no need to transparently proxy things to the leader; this is done for you by a bolt+routing driver, as Michael says. But it is also OK to use a GraphDatabaseService to do transactions. I think the missing piece here is that if I did transactions inside of a GraphDatabaseService, I'd generally be only doing them on the leader, automatically, with no extra code needed, because whatever that code is would only be invoked on the leader.
For example: you write a stored procedure with the annotation
@Procedure(name = "myPlugin.writeSomeStuff", mode = Mode.WRITE)
Inside of that procedure, you use a GraphDatabaseService to write some stuff, and then stream some results back. All good.
Now, that plugin is installed on all 3 nodes, but the procedure never gets called anywhere but the leader, because the client writes explicit write transactions (and autocommit) transactions to the leader. So cypher that calls the procedure in addition to bolt+routing basically takes this away so you don't have to worry about it.
If you did manually call that write procedure on a follower, it would fail -- because followers cannot accept writes. Fortunately if you set things up right, this just won't arise. Extra cores and read replicas scale out your read workload.