This is part 5 of the Musaned series. Part 4 covered the Oracle integration.
If Oracle was the first foreign country Musaned's PHP code had to visit, Microsoft SQL Server was the second — same passport problems, different border control. The data at stake this time: fiber customer information, which business teams needed to see alongside everything else Musaned already served.
A different flavor of dependency pain
PHP's route into MSSQL is the sqlsrv / pdo_sqlsrv extension pair, and just like oci8 it refuses to exist on its own. The chain this time: the extensions require Microsoft's ODBC Driver for SQL Server, which requires unixODBC, which requires accepting Microsoft's package repositories into your Linux server's sources — plus the usual dance of matching the extension version to your exact PHP version. Different vendor, same lesson: the driver setup is a project of its own, and it must be scripted, not remembered.
To Microsoft's credit, once the repository is configured the install is more package-manager-friendly than Oracle's zip-file archaeology. But the failure modes are sneakier. A missing ODBC driver produces connection errors that look like network problems. TLS negotiation between a modern OpenSSL and an older SQL Server can fail with messages that point everywhere except the real cause. When — not if — you hit these, the debugging order that saves hours is: verify the ODBC layer alone first (isql is your friend), then the PHP extension, then Laravel's connection config. Layer by layer, never all at once.
Reading someone else's database politely
The technical connection was half the work. The other half was a rule I set early and never regretted: Musaned is a guest in that database. The fiber customer store belongs to another system with its own lifecycle, its own team, and its own performance budget. Being a good guest meant:
Read-only access, enforced at the credential level. Not "we promise not to write" — the account physically cannot.
Queries stay narrow. We ask for exactly the columns and rows a feature needs, never "select everything and figure it out in PHP."
Caching absorbs repeat traffic. Redis (more on this in the next post) sits in front of the frequent lookups, so a screen refresh in Musaned doesn't become load on a production system we don't own.
Their schema is not our contract. Everything read from MSSQL passes through a mapping layer into Musaned's own shapes, so when the source system changes a column, the blast radius is one service class — not every screen that touches fiber data.
The pattern that was forming
By the second external database, the shape of every future integration was becoming obvious: painful driver setup you automate once; a dedicated service class that owns the connection, the queries, and the mapping; caching where repetition is high; and strict respect for the source system's boundaries. In the next post I'll formalize that into the playbook that carried Musaned through all the integrations that followed — and how queue design kept the heavy ones from stepping on daily operations.
Next in the series: one service class per external system — the integration playbook.
Leave a Reply
Your email address will not be published.