Clojure's core.async channels provide a great metaphor for joining components in a system. But I noticed there is a misconception floating around: that channels fully decouple system components, so that components do not need to know anything about one another. As a result, it's easy to go overboard with core.async. I've seen people overuse channels, putting them everywhere, even when a cross-namespace function call would do just fine.
What channels do provide is asynchronous processing. The "degree" of asynchronicity can be controlled — we may choose to always block the caller until someone reads from a channel (thus providing a rendezvous point), or we may choose to introduce asynchronicity, letting the caller proceed without worrying about when the value gets processed.
Since you can put anything onto a channel, it's easy to forget that this "anything" is part of the protocol. Just as functions have an argument signature, channels have value signatures, or protocols: the common data format that all parties agree on.
It isn't true that channels fully decouple components, so that "they don't need to know anything about one another". You still need a wire protocol, just as with directly calling functions in another component. Channels do decouple things in time: you are not forced to a synchronous execution model and can control when things are being processed. But they are not a magic component decoupling wand, so don't use them when a simple synchronous function call will do.