I do something like this with some of our products.
We have both an update and full install available for one of them. Since the two file groups are grossly different, I have two separate projects, but I use the same code. The only difference between the two setup.rul files is the assignment of bFull to be false for the update and true for the full install. I then have subsequent checks within the code to do one thing or the other depending upon which scenario I'm in. This has worked quite well.
Another version of this same product has both a keyed and non-keyed full install. This one's more like your situation as they are only different in their applications. Since I want the two to have the same GUID, I use the same project and just manually change the included file groups. As before, I also have a bKeyed which lets me know which context I'm in. Finally I have two different media definitions so as to not overwrite them and to help manage which is which. In time I thought about creating additional ghost components for these file groups which I would include or exclude automatically through the code depending upon the value of bKeyed.
Another way to ease maintenance, which I haven't gotten into yet, is to create common code and use the #include functionality to make it available. You can make it more versatile by expanding the paramaters that these functions take and the like.
Hope it helps.