How does Bitcoin know if enough BTC are available for an account to allow a transaction? Let's consider this simplified blockchain:

BLOCK 108
address ddd111 received 2 BTC by mining block 108

BLOCK 109
address abc123 received 2 BTC by mining block 109

BLOCK 110
address xyz999 created with 0 BTC
address fff555 created with 0 BTC
address abc123 =&gt;  xyz999   1 BTC
address abc123 =&gt;  fff555   1 BTC

BLOCK 111
address ddd111 =&gt; xyz999    1 BTC

BLOCK 112
address xyz999 =&gt; bbb888    5 BTC


How does Bitcoin Core knows there's a problem in the last transaction? How does it know that xyz999 cannot send 5 BTC to bbb888 because xyz999's balance is not enough?
Bitcoin doesn't track a balance on an address.  You can get a balance but that is for humans and isn't how the protocol really works.  What bitcoin nodes do track is a list of unspent transaction outputs.

Here is your scenario reworded a bit.

BLOCK 108

address ddd111 received 2 BTC by mining block 108 in transaction A:0
(By this I mean transaction A has one output (index 0) that is 2 BTC to address ddd111)

Transaction A:
Inputs: coinbase
Outputs: A:0 - ddd111 2.0 BTC


BLOCK 109

address abc123 received 2 BTC by mining block 109 in transaction B

Transaction B
Inputs: coinbase
Outputs: B:0 - abc123 2.0 BTC


BLOCK 110

The network knows nothing about address with no funds, this doesn't happen

address xyz999 created with 0 BTC 
address fff555 created with 0 BTC


address abc123 spends B:0, sending 1 BTC to xyz999 (C:1)  and remainder to self (1 BTC) (C:0) transaction C

Transaction C
Inputs: B:0 (signed by abc123)
Outputs: C:0 - abc123 1.0 BTC
         C:1 - xyz999 1.0 BTC


address abc123 spends C:0, sending 1 BTC to fff555 transaction D

Transaction D
Inputs: C:0 (signed by abc123)
Outputs: D:0 - fff555 1.0 BTC


BLOCK 111

address ddd111 spends transaction A sending xyz999  1 BTC and 1 BTC back to self - transaction E

Transaction E
Inputs: A:0 (signed by ddd111)
Outputs: E:0 - xyz999 1.0 BTC
         E:1 - ddd111 1.0 BTC


BLOCK 112

address xyz999 spends transaction E:0 and C:1 to send bbb888 2 BTC - transaction F

Transaction F
Inputs: E:0 (signed by xyz999)
        C:1 (signed by xyz999)
Outputs:
        F:0 - bbb888 2.0 BTC


(xyz999 couldn't form a valid transaction sending 5 BTC because there aren't 5 BTC of unspend outputs for it to use)

Note: A transaction can have multiple outputs which can be spent independantly.



Now we have this list after block 112:

Unspent after block 112
D:0
F:0
E:1


Each node's job is to import the block by marking the spent outputs as spent and tracking the new unspent outputs.  This is part of why it takes time to sync the blockchain, not only do the blocks need to be downloaded but they need to be validated and the local database needs to be updated.

Note: if you run a node that prunes, it will just keep the unspent outputs. If you don't prune, it will keep all blocks and the unspent outputs.