I suppose the fundamental difference here is that a transaction is an atomic thing already - it either makes sense as a whole against a value of db, or it doesn’t. Consequently, the job of the “do this”, “put some data here”, “put some data there” is to accumulate speculative changes in the form of data for a transaction.
with approach is arguably redundant here, as even if your
with calls have made sense against that value of the db locally, there’s no guarantee they will when you really transact them. It feels like a postgres-ism (or similar) that doesn’t really make sense in a datomic world.
For example, and as I guess you know, postgres has things like isolation levels, distributed locks etc to allow imperative sequences of calls to be treated as a reversible transaction (with a cost!), but that just doesn’t equate here.
About your point 2, creating and referencing entities in a transaction, this is why tempids exist, no?
On point 3, wouldn’t that throw due to conflicts in the transaction? That said, having to worry about asserting and retracting the same entity in the same transaction feels like something isn’t quite right in your data flow, and could be elided before submitting a transaction, or be an invariant, for example.