welcome to Cinder and its forums.
Your question about what's the best approach to writing a large application has an easy answer: there is no best approach, at least not a single best approach. Each challenge requires its own solution and it takes practice and experience to arrive at that solution.
Sometimes, you stumble upon a solution that works for most of your projects, though. Most of the projects I did in Cinder have the same structure. It's based on the concept of the separate
draw() methods that Cinder exposes. After settings things up, I continuously call each object's
update method to advance their state and/or animation, then call each object's
draw method to render what needs to be rendered.
At the core sits a system that could be dubbed "scene graph", which is just a rather large set of objects in a hierarchical structure. Objects can have one parent, but multiple children. You then organize your application by creating container classes (e.g. a window, a menu bar, a "game") and item classes (a button, a menu item, an "enemy") and arrange them into the hierarchical structure that defines your 2D interface, or your game. All classes have both an
draw method. You call them by starting at the topmost instance and then continue down the hierarchy. Some items will be invisible or inactive, in which case you can ignore them. Their children will be automatically ignored as well and won't be updated or drawn. Some items will spawn new ones, or destroy others. The structure will change all the time.
A system like that is easy to maintain and expand, because each class is relatively small and contained. It can have its own state (e.g. opened or closed for menu items, or shooting and exploding for enemy items). Its behavior is defined in the class as well, and you can expose methods like
explode() to control their actions. Your application will not be one huge script, but a number of smaller, easier to maintain scripts.
A word about design patterns and state machines: while each of them offers an elegant solution to a particular problem, they are just that: one solution for one, well-defined problem. Knowing what they were designed for, why it works and why it's a good solution, is very valuable. Implementing them, however, especially in a language like C++, can be very tricky (one example being the Singleton pattern, which is notorious in C++ land). Choosing which pattern to use for your application is hard and you'll often find that it won't solve all your problems.
My suggestion is to use your own creative mind, your experience as a programmer and some of the thoughts and ideas that you find around the internet to come up with a solution that works for your particular, truly unique challenge. Most likely this will not be an off-the-shelf design pattern. Counter-intuitively, the most important goal of your project is not a bug-free application or a happy customer, it's a maintainable, flexible, stable application. Only then will you be able to change and improve things on request, and so will others who have to read and expand your code. Write your big application as a collection of smaller components, some using a state machine, others using an enum or a couple of booleans, depending on the individual class. Choose what works best for you.