Indeed complexity is there for most real domains. But DSL is meant to be another abstraction layer on top of the complexity. My lesson is constraint is the most important as this lays the foundation of its usefulness. I don't mean you should not have things like branch or loop. You certainly could add them, just in a domain friendly way that doesn't hurt the readability and writability of your DSL.
In order to tackle arbitrary complexity, the DSL itself should be designed with extensibility in mind so that technical people could easily add language constructs or vocabularies by using script or general purpose language. Further, those features should be able to be turned off for some scenarios. For those inherently complex stuff, they should be left to general purpose language anyway.
Tooling support is another big problem. In my opinion, DSL is never just the tiny language, it is about the language and tooling. However the tooling requirement is often not as standard as general purpose language. Domain users normally don't need an IDE(an editor should be generally required) or debugger. Rather, they want something that can be seamlessly integrated with the host application.
And more over, different people (even in the same domain) may have different requirements on DSL maintenance, eg, governance, versioning. This often leads to a case by case customization. I do not have a good answer for this other than the general advice: treat DSL and tooling as a whole and make the API as easy as possible for integration.
Edit: For anyone interested in my DSL(still work in progress), here is a glimpse: