Documenting legacy code-bases
I would highly recommend following the scout rule with legacy code-bases.
Trying to document a legacy project independently of working on it will just never happen. Even if you get in contractors to do it, as soon as they finish the project, that documentation will start falling behind all over again, because developers haven't got into the habit of updating it.
In-code documentation
The most important thing is to use the documentation facilities in your chosen development environment, so that means pydoc for python, javadoc in java or xml comments in C#. These make it easy to write the documentation at the same time as writing the code.
If you rely on coming back and documenting things later, you may not get around to it, but if you do it as you are writing the code, what needs to be documented will be fresh in your mind. C# even has the option to issue a compilation warning if the XML documentation is incomplete or inconsistent with the actual code.
Also, if reviewing documentation becomes part of your code review process, everyone can be encouraged to contribute, fostering a sense of ownership of the documentation as well as of the code.
Tests as documentation
Another important aspect is having good integration and unit tests.
Often documentation concentrates on what classes and methods do in isolation, skipping over how they are used together to solve your problem. Tests often put these into context by showing how they interact with each other.
Similarly, unit-tests often point out external dependencies explicitly through which things need to be Mocked out.
I also find that using Test-driven development I write software which is easier to use, because I'm using it right from the word go. With a good testing framework, making code easier to test and making it easy to use are often the same thing.
Higher level documentation
Finally there is what to do about system level and architectural documentation.
Many would advocate writing such documentation in a wiki or using Word or other word processor, but for me the best place for such documentation is also alongside the code, in a plain text format that is version control system friendly.
Just like with in-code documentation, if you store your higher level documentation in your code repository then you are more likely to keep it up to date. You also get the benefit that when you pull out version X.Y of the code, you also get version X.Y of the documentation. In addition, if you use a VCS friendly format, then it means that it is easy to branch, diff and merge, just like your code.
Not only that, but if you use something like readthedocs then you can publish version specific documentation for each software release.
I quite like rst, as it is easy to produce both html pages and pdf documents from it, and is much friendlier than LaTeX, yet can still include LaTeX math expressions when you need them.
The core doesn't know where the data comes from, right ? So, since all data inputs must be sanitized, I'd say that's it's a must for the CORE to validate the inputs.
That being said, you'd have to create an API where you expect to receive bad input and return something meaningful for the caller, and not just throwing weird and cryptic exceptions everywhere. "Garbage in, garbage out" is just a bad API design.
One approach I've used in the past with a few MVC5 applications was to create a "BusinessException", which contained some key information for the caller to understand that they've been using the API wrong. One of the components of the BusinessException was a "user friendly error message", that could be directly shown to a user either in a Forms app or a Web app (the core never knew where it was running), along with some other important fields such as "FaultyProperty", "TechnicalException" and so on.
That way you can make your MVC app a thin layer doing basic validation (very basic) before dispatching the heavy load to the Core, and keep a single idiomatic way to handle errors by catching the "BusinessException" when needed and reacting accordingly.
This way you get to keep the single responsibility principle and DRY in place while still holding your validations and keeping code easy to read.
And by giving details of the error back to the caller by filling specific fields/properties in the BusinessException, the GUI layer can highlight whatever it needs to in order to provide a nice user experience.
Best Answer
The truth is: currently, that is something that modern software development is lacking - a consensus about modeling. UML seems to be some kind of smallest common divisor, but in reality there is only consensus about the notation, not about the semantics. There exists dozens of different opinions on how UML should be interpreted to create code (perhaps you can find one interpretation that is ok for your team).
On the other hand, there is a holy war going on between those "agile" people saying "don't make formal models, better write working code" and those "BDUF" (big design up front) people who are thinking tools like "MDA" (model driven architecture) are the solution.
Other people (re-)discovered flow based programming for modern software design as an alternative for UML. Read here and here to find out more about that.